In this exercise, we will pull together many aspects of what we have
learned so far to calibrate the HBV model to weighted data from the
Fraser Experimental Forest in Colorado using precipitation/runoff and
potential evapotranspiration (PET) data derived from the methods
explored in the snowmelt and evapotranspiration modules.
HBV is: In the previous labs, we have worked with single-process
models. HBV is more complex process-based model using many parameters to
describe the physical processes of the hydrologic cycle https://rpubs.com/sdhakal/848236
From: Durga Lal Shrestha & Dimitri P. Solomatine (2008)
Data‐driven approaches for estimating uncertainty in rainfall‐runoff
modelling, International Journal of River Basin Management, 6:2,
109-122, DOI: 10.1080/15715124.2008.9635341
HBV versions with a GUI exists, but the data manipulation is more
convenient in a coding environment, and we want you to be able to see
‘under the hood’. Instead of dealing with the inner workings of the
model too much, we will put the focus on importing and organizing the
data, getting the model running, manual optimization.
The HBV realization we will be using is contained in the R package
TUWmodel. Please search that package in your browser and familiarize
yourself with it (that will be the first step). TUW simply means
“Technische Universitaet Wien”, or Vienna University of Technology. The
model is slightly different from the original HBV but still similar
enough to call it HBV. We also need to understand the arguments and
parameters required for the model.
Type ‘?TUWmodel’ in your console. The package comes with an example
dataset, and you can run examples either by copying and pasting the
data(), TUWmodel() and plots into your console, or you can find and
click ‘Run examples’ under the Examples header and view
the script and plot outputs in the Help window. The first example shows
how the ‘TUWmodel’ can be run given specifications for a long set of
parameters (param). You can provide the model with precip, air
temperature, area and ET along with the parameter set and it will
simulate SWE, snowmelt and discharge. We can obtain measurements for
each of these variables, so we will import all of this data and test the
fit of the simulated data.
Labwork (20 pnts)
Our first steps will be to import and format the data. As you might
recall from the previous modules, this step can require the most script
and ‘work’, but is critical to a valid model output. In this assignment,
we have simplified much of the data collection for you, as the steps are
ones you have already completed in previous modules. In the .csv
imported below, we started by collecting the same SNOTEL data that you
used in the snowmelt module, but downloaded for a greater date range
(water years 2018-2022) (date, SWE depth in mm, and daily precipitation
(mm)) However, you will notice that the column names of the provided
table contain ‘weighted_’.
# Import data
indata <- read.csv("HBV_data/weighted_data_fool_HBV.csv")%>%
mutate(date = ymd(date)) %>%
rename(Q_m3_s = m3_s, Q_mm_day = mm_day, runoff_input.mm = input.mm )
str(indata)
'data.frame': 1826 obs. of 17 variables:
$ date : Date, format: "2017-10-01" ...
$ wtr_yr : int 2018 2018 2018 2018 2018 2018 2018 2018 2018 2018 ...
$ weighted_swe.mm : num 13.3 46.7 53.4 53.4 51.2 ...
$ weighted_precip.mm: num 26.78 7.65 1.91 0 0 ...
$ weighted_pcumul.mm: num 26.8 34.4 36.3 36.3 36.3 ...
$ Q_m3_s : num 0.00862 0.00743 0.00715 0.00704 0.00704 ...
$ Q_mm_day : num 0.282 0.243 0.234 0.23 0.231 ...
$ Tmax_c : num 5.55 1.95 5.25 12.75 12.75 ...
$ Tmean_c : num 0.8 -1.6 -0.7 6.2 6.65 0.25 3.2 2.5 -7.8 -1.8 ...
$ Tmin_c : num -3.95 -5.15 -6.65 -0.35 0.55 ...
$ RHmin : num 43.1 60.3 41.2 28.9 27.4 ...
$ RHmax : num 90.6 91.8 91.2 86 78.6 ...
$ month : int 10 10 10 10 10 10 10 10 10 10 ...
$ day : int 1 2 3 4 5 6 7 8 9 10 ...
$ SWEdiff.mm : num 0 33.38 6.75 0 -2.28 ...
$ Pdiff.mm : num 0 7.65 1.91 0 0 ...
$ runoff_input.mm : num 0 0 0 0 2.28 ...
As we noted in the Snowmelt module, factors such as elevation and
vegetation will affect the of snow-to-runoff rate. Similarly,
temperature and precipitation differ with elevation, meaning that SNOTEL
data from a single location may not fully represent conditions across
the entire watershed.
To account for these variations, the “weighted” values for this
exercise have been adjusted using linear scaling relationships that
estimate average conditions across the watershed. The adjustments use
elevation-precipitation and elevation-temperature relationships derived
from inverse distance weighting to better approximate spatially
distributed hydrological inputs rather than relying on a single point
measurement.

If you are interested, you can start exploring spatial interpolation
with: 1. Thiessen polygons
2. Inverse distance weighting 3. Kringing
methods to determine if any of these are applicable to your study
area.
Other columns in this data include: Q_m3_s and
Q_mm_day - Discharge (Q) collected at the Fool Creek outlet by
USFS, in units of cubic meters per second and mm per day from April
until October. Tmax_c, Tmin_c and Tmean_c - Typically,
we could find daily mean, max and minimum temperatures in the SNOTEL
datasets, however, this particular station is missing temperature data
(due to restrictions on technical access in 2020), so we retrieved
temperature data from GridMET through Climate Engine.
This highlights the importance of evaluating each variable for
completeness. It will save you the headache of running the entire
workflow, only to find that model outputs cannot be simulated for the
later half of 2020 due to missing input data. RHmin,
RHmax - Relative humidity daily min and max, also from GridMET,
accessed through Climate Engine SWEdiff.mm, Pdiff.mm and
runoff_input.mm - all daily outputs of a temperature based
snowmelt model (the same as in the snowmelt module). runoff_input is the
estimated daily input to the stream from melted snow and liquid
precipitation combined.
Keep in mind that you can plot more than two variables in a single
plot, but they will be most helpful if you group variables with a
similar y-scale. For example, cumulative precipitation or SWE values
will have a different range than variables that represent daily
measurements like ‘input_mm’. Alternatively, you can add a secondary
y-axis to your plot.
ggplot(data = indata) +
geom_point(aes(x = date, y = runoff_input.mm, color = "runoff_input.mm"), size = 0.5) + # Assign a label for legend
geom_point(aes(x = date, y = Q_mm_day, color = "mm/day"), size = 0.5) + # Assign a different label
geom_point(aes(x = date, y = weighted_precip.mm, color = "weighted_precip.mm"), size = 0.5) + # Assign a different label
geom_point(aes(x = date, y = Pdiff.mm, color = "Pdiff.mm"), size = 0.5) +
scale_color_manual(values = c("runoff_input.mm" = "blue", "mm/day" = "red", "weighted_precip.mm" = "green", "Pdiff.mm" = 'purple')) + # Customize colors
labs(color = "Discharge Units") + # Legend title
theme_minimal()

2,930m. watershed_area_m2 <- 2640000
Note that while the stream is snow-covered, there are no stage/flow
measurements being made at this site. For many of our calculations to
work, we will not want NA in our data frames. In this data set, if we
view the tabular data, the end discharge reading (Q_mm_day), is very
similar to the first in the following calendar year. For this example,
we will then fill the NA values with the mean of the final and first
readings for each winter (Oct - April) NA string.
# Rather than scrolling through a dataframe, this gives us a sum of 'NA' ros in this column
sum(is.na(indata$Q_mm_day))
[1] 917
# Function to fill NA values in Q_mm_day during winter (Oct - Apr)
fill_na_winter <- function(df) {
df <- df %>%
arrange(date) %>% # Ensure data is sorted
mutate(
month = month(date),
is_winter = month %in% c(10, 11, 12, 1, 2, 3, 4) # Identify winter months
)
# Identify NA stretches in winter months
na_indices <- which(is.na(df$Q_mm_day) & df$is_winter)
for (idx in na_indices) {
# Find the last non-NA before the current NA
prev_value <- df$Q_mm_day[max(which(!is.na(df$Q_mm_day[1:(idx - 1)])))]
# Find the next non-NA after the current NA
next_value <- df$Q_mm_day[min(which(!is.na(df$Q_mm_day[(idx + 1):nrow(df)])) + idx)]
# Replace the NA with the average of prev_value and next_value
if (!is.na(prev_value) & !is.na(next_value)) {
df$Q_mm_day[idx] <- (prev_value + next_value) / 2
}
}
return(df)
}
# Apply the function
indata <- fill_na_winter(indata)
Let’s try our quick check again:
# Now we should see zero NA in this column
sum(is.na(indata$Q_mm_day))
[1] 0
Next, we need to add daily potential evapotranspiration (PET) to the
dataset. The evapotranspiration module covered some of the numerous
pre-built functions available in various R packages designed for ET
estimation. For instance, the ‘Evapotranspiration’ package provides ET
estimates derived from approximately 20 distinct equations or
variations. These functions require precise data formatting to ensure
compatibility with the function arguments. To help you structure your
function inputs similarly, many packages come with example datasets in
their documentation. SPEI is another package that offers ET functions.
The github repository for this package has been updated fairly recently,
which can be important to verify.
We chose to write our own function here so you could ‘see under the
hood’.
Not all packages are equally maintained, as they are often developed by
researchers or modelers to improve the repeatably of their work, and
contribute to the broader scientific community. Once funding ceases, or
if the original developer moves on to new projects, a custom package may
no longer receive updates or support. As R, RStudio and other supporting
packages are updated, a package may become depreciated. If the package
still functions correctly in your current R version and dependencies,
there’s no immediate reason to stop using it. However, if you are
concerned that future versions of R or dependencies might break the
packages you use, you can check the development and maintenance history
of custom or specialized packages (or write a package or function for
yourself as exemplified below!).
Here is our Hargreaves function, adapted from the
‘Evapotranspiration’ package to minimize re-formatting of our
dataframe.
# Function to calculate ET
calculate_ET <- function(data, constants, ts = "daily", message = "yes", save.csv = "no", ...) {
# Check for required data
if (is.null(data$Tmax) | is.null(data$Tmin)) {
stop("Required data missing for 'Tmax' and 'Tmin', or 'Temp'")
}
# Hargreaves-Samani ET Calculation
Ta <- (data$Tmax + data$Tmin) / 2
P <- 101.3 * ((293 - 0.0065 * constants$Elev) / 293) ^ 5.26
delta <- 4098 * (0.6108 * exp((17.27 * Ta) / (Ta + 237.3))) / ((Ta + 237.3) ^ 2)
gamma <- 0.00163 * P / constants$lambda
d_r2 <- 1 + 0.033 * cos(2 * pi / 365 * data$J)
delta2 <- 0.409 * sin(2 * pi / 365 * data$J - 1.39)
w_s <- acos(-tan(constants$lat_rad) * tan(delta2))
N <- 24 / pi * w_s
R_a <- (1440 / pi) * d_r2 * constants$Gsc * (w_s * sin(constants$lat_rad) * sin(delta2) +
cos(constants$lat_rad) * cos(delta2) * sin(w_s))
C_HS <- 0.00185 * (data$Tmax - data$Tmin) ^ 2 - 0.0433 * (data$Tmax - data$Tmin) + 0.4023
ET_HS.Daily <- 0.0135 * C_HS * R_a / constants$lambda * (data$Tmax -
data$Tmin) ^ 0.5 * (Ta + 17.8)
ET.Daily <- ET_HS.Daily
# Create YearMonth Column
data$YearMonth <- as.Date(paste(year(data$Date.daily), month(data$Date.daily), "01", sep = "-"))
# Annual and Monthly Aggregations
ET.Annual <- aggregate(ET.Daily ~ year(YearMonth), data = data, FUN = sum)
ET.Monthly <- aggregate(ET.Daily ~ YearMonth, data = data, FUN = sum)
# ET formulating
ET_formulation <- "Hargreaves-Samani"
ET_type <- "Reference Crop ET"
results <- list(ET.Daily = ET.Daily, ET.Monthly = ET.Monthly,
ET.Annual = ET.Annual, ET_formulation = ET_formulation,
ET_type = ET_type)
# Save to CSV if required
if (save.csv == "yes") {
for (i in 1:length(results)) {
namer <- names(results[i])
write.table(as.character(namer), file = "ET_HargreavesSamani.csv",
dec = ".", quote = FALSE, col.names = FALSE,
row.names = F, append = TRUE, sep = ",")
write.table(data.frame(get(namer, results)), file = "ET_HargreavesSamani.csv",
col.names = F, append = TRUE, sep = ",")
}
invisible(results)
} else {
return(results)
}
}
Now we will format the inputs so the data is easily read by the
function, and run the function.
# Format our data to fit the function
PET_data <- list(
Tmax = indata$Tmax,
Tmin = indata$Tmin,
J = as.numeric(format(indata$date, "%j")),
Date.daily = indata$date
)
# Define constants
constants <- list(
Elev = 2900, # Elevation in meters
lambda = 2.45, # Latent heat of vaporization in MJ.kg^-1
lat_rad = 39.88 * pi / 180, # Latitude in radians
Gsc = 0.0820 # Solar constant in MJ.m^-2.min^-1
)
PET_Hargreaves <- calculate_ET(
data = PET_data,
constants = constants,
ts = "daily", # Optional; defaults to "daily"
message = "yes", # Optional; prints summary
save.csv = "no" # Optional; do not save results to a CSV
)
Now we’ll add daily ET into our original dataframe:
PET_mm <- PET_Hargreaves$ET.Daily
# put the approximated PET_mm into the larger indata df
indata <- cbind(indata, PET_mm) #with cbind
head(indata)
# Annual summary stats
indata_analysis <- indata %>%
select(-date) %>%
group_by(wtr_yr) %>%
summarise(
weighted_precip.mm = sum(weighted_precip.mm, na.rm = TRUE),
Tmean_c = mean(Tmean_c, na.rm = TRUE),
swe.mm_max = max(weighted_swe.mm, na.rm = TRUE),
pcumul.mm_max = max(weighted_pcumul.mm, na.rm = TRUE),
Tmax = max(Tmax_c, na.rm = TRUE),
Tmin = min(Tmin_c, na.rm = TRUE),
Q_mm_sum = sum(Q_mm_day, na.rm = TRUE),
PET_mm_sum = sum(PET_mm, na.rm = TRUE),
runoff_input.mm = sum(runoff_input.mm, na.rm = TRUE),
)
indata_analysis
NA
# Your script below should fit your variables from your summary dataframe above. Here is an example of what a simple water balance might look like if I named my summary dataframe indata_analysis:
# Calculate the residual water after accounting for PET and discharge
residual_water <- indata_analysis$runoff_input.mm - (indata_analysis$PET_mm_sum + indata_analysis$Q_mm_sum)
# View the residual for each year
print(residual_water)
[1] -245.4585632 0.5648919 -184.4922861 -231.7575558 -219.0837730
By subtracting discharge and PET from runoff_input, we’re essentially
examining the residual water. This could represent the amount of water
available to the system after accounting for the demand (PET) and the
outflow (discharge).
# make long form with PETsum and Psum as the key-value pairs (exclude wtr_yr from )
dat_sum_plot <- indata_analysis %>%
pivot_longer(names_to = "key", values_to = "value", -wtr_yr)
# bar plot pf PET and P for each year
ggplot(dat_sum_plot, aes(x = wtr_yr, y = value, fill = key)) +
geom_bar(stat = "identity", position = "dodge") +
labs(x = "Water Year", y = "mm/year", fill = {})

### TIMING
# create weekly means or totals for both time series and plot them together to determine the timing of each
dat_weekly <- indata %>%
group_by(Week = week(date)) %>%
summarise(
PET = sum(PET_mm),
P = sum(runoff_input.mm)
) %>%
pivot_longer(names_to = "key", values_to = "value", -Week)
# line plot of P and PET on a weekly basis. Use dat_weekly as the data source.
ggplot(dat_weekly, aes(x = Week, y = value, color = key)) +
geom_line() + # create line plot
labs(x = "Week of Year", y = "mm/year", color = {})

single model execution
# set up the parameter vector
model_params <- c(
1.05, # SCF snow correction factor [-] (e.g., 0.9-1.5);
1.80, # DDF degree day factor [mm/degC/timestep] (e.g., 0.0-5.0 mm/degC/day);
2, # Tr threshold temperature above which precipitation is rain [degC] (e.g., 1.0-3.0 degC);
0, # Ts threshold temperature below which precipitation is snow [degC] (e.g., -3.0-1.0 degC);
-0.336, # Tm threshold temperature above which melt starts [degC] (e.g., -2.0-2.0 degC);
0.2, # LPrat parameter related to the limit for potential evaporation [-] (e.g., 0.0-1.0);
121, # FC field capacity, i.e., max soil moisture storage [mm] (e.g., 0-600 mm);
2.52, # BETA the non linear parameter for runoff production [-] (e.g., 0.0-20.0);
0.473, # k0 storage coefficient for very fast response [timestep] (e.g., 0.0-2.0 days);
9.06, # k1 storage coefficient for fast response [timestep] (e.g., 2.0-30.0 days);
142, # k2 storage coefficient for slow response [timestep] (e.g., 30.0-250.0 days);
50.1, # lsuz threshold storage state, i.e., the very fast response start if exceeded [mm] (e.g., 1.0-100.0 mm);
2.38, # cperc constant percolation rate [mm/timestep] (e.g., 0.0-8.0 mm/day);
10, # bmax maximum base at low flows [timestep] (e.g., 0.0-30.0 days);
25 # croute free scaling parameter [timestep^2/mm] (e.g., 0.0-50.0 days^2/mm);
)
# set time period
model_in <- indata %>%
filter(date >= as_date("2017-10-01") & date <= as_date("2022-09-30"))
# set up the model
## THIS IS THE ACTUAL MODEL EXECUTION
modelRun <- TUWmodel(
prec = model_in$weighted_precip.mm, # precip input
airt = model_in$Tmean_c, # air temp input
ep = model_in$PET_mm, # pet input
area = 1, # one zone for the entire watershed
param = model_params # input model parameters
)
# get all outputs into a nice df
HBVRun <- tibble(
Date = model_in$Date, # date
P_mm = modelRun$prec, # precip
Tair = modelRun$airt, # air temp
SWEobs = model_in$weighted_swe.mm, # observed swe
PET = modelRun$ep,# pet
Qobs = model_in$Q_mm_day, # observed discharge
Qsim = modelRun$q[1, ], # simulated discharge
Qsurf = modelRun$q0[1, ], # surface runoff
Qsubsurf = modelRun$q1[1, ], # subsurface flow
Qbase = modelRun$q2[1, ], # groundwater flow
Rain = modelRun$rain[1, ], # simulated rain
Snow = modelRun$snow[1, ], # simulated snowfall
Melt = modelRun$melt[1, ], # simulated melt
SWEsim = modelRun$swe[1, ], # simulated swe
Soilmoist = modelRun$moist[1, ], # simulated soil storage
AET = modelRun$eta[1, ], # simulated evapotranspiration
StorageUpper = modelRun$suz[1, ], # upper storage value
StorageLower = modelRun$slz[1, ] # lower storage value
)
Objective functions
KGE
Before we can start trying to tune our model to look more like the
observed discharge record, it would be helpful to have some sort of
quantified metric for how well our modeled data fits the measured
data.
There are many different ways to do this, but discussion of the pros
and cons of those approaches is beyond this quick introduction to
modeling.
Here we will demonstrate the Kling-Gupta efficiency both for runoff
as well as for swe.
# select the Qobs and Qsim timeseries
# DON'T FORGET TO EXCLUDE THE FIRST YEAR
Qobs <- HBVRun$Qobs[366:length(HBVRun$Qobs)] # observed runoff WITHOUT WARM-UP PERIOD
Qsim <- HBVRun$Qsim[366:length(HBVRun$Qsim)] # simulated runoff WITHOUT WARM-UP PERIOD
# KGE
kge_r_q <- cor(Qobs, Qsim)
kge_beta_q <- mean(Qsim) / mean(Qobs)
kge_gamma_q <- (sd(Qsim) / mean(Qsim)) / (sd(Qobs) / mean(Qobs))
kge_q <- 1 - sqrt((kge_r_q - 1)^2 + (kge_beta_q - 1)^2 + (kge_gamma_q - 1)^2)
kge_q
[1] 0.4775681
## Snow - water equivalent
SWEobs <- HBVRun$SWEobs[366:length(HBVRun$SWEobs)] # observed swe WITHOUT WARM-UP PERIOD
SWEsim <- HBVRun$SWEsim[366:length(HBVRun$SWEobs)] # simulated swe WITHOUT WARM-UP PERIOD
# KGE
kge_r_swe <- cor(SWEobs, SWEsim)
kge_beta_swe <- mean(SWEsim) / mean(SWEobs)
kge_gamma_swe <- (sd(SWEsim) / mean(SWEsim)) / (sd(SWEobs) / mean(SWEobs))
kge_swe <- 1 - sqrt((kge_r_swe - 1)^2 + (kge_beta_swe - 1)^2 + (kge_gamma_swe - 1)^2)
kge_swe
[1] 0.563363
NSE
Nash-Sutcliffe Efficiency (NSE).
Basically, the NSE looks at how much better your model run did that
if you had just used the mean discharge for the data record as your
“modelled results”. It does this by comparing how far off the observed
values where from the mean discharge to how far off the modeled values
were from the observed discharge.
Mathematically, NSE is the sum of the squared differences between the
modeled and observed discharge divided by the sum of the squared
differences between the observed and mean discharge, subtracted by
1.
\[
NSE = 1 - \frac{\sum_{t = 1}^{T}{(Q_m^t - Q_o^t)^2}}{\sum_{t =
1}^{T}{(Q_o^t - \bar{Q_o})^2}}
\] Where \(Q_m^t\) is modeled
discharge at time t, \(Q_o^t\) is
observed discharge at time t, and \(\bar{Q_o}\) is mean observed discharge.
#Calculate NSE for snow, SWE is modeled, STA2 is measured
NSE_Q <- 1 - ((sum((HBVRun$Qobs - HBVRun$Qsim) ^ 2)) /
sum((HBVRun$Qsim - mean(HBVRun$Qsim)) ^ 2))
NSE_Q
[1] 0.1264142
#Calculate NSE for snow, SWE is modeled, STA2 is measured
NSEsno <- 1 - ((sum((HBVRun$SWEobs - HBVRun$SWEsim) ^ 2)) /
sum((HBVRun$SWEsim - mean(HBVRun$SWEsim)) ^ 2))
NSEsno
[1] -0.1020562
Calibrate HBV manually
Woohoo! We can now run our model and assess how well it is
working!
Now, let’s see how well we can get it to work. The code below runs
the model, produces a plot, and calculates the NSE based on
discharge.
# set up the parameter vector
model_params <- c(
1.05, # SCF snow correction factor [-] (e.g., 0.9-1.5);
1.80, # DDF degree day factor [mm/degC/timestep] (e.g., 0.0-5.0 mm/degC/day);
2, # Tr threshold temperature above which precipitation is rain [degC] (e.g., 1.0-3.0 degC);
0, # Ts threshold temperature below which precipitation is snow [degC] (e.g., -3.0-1.0 degC);
-0.336, # Tm threshold temperature above which melt starts [degC] (e.g., -2.0-2.0 degC);
0.2, # LPrat parameter related to the limit for potential evaporation [-] (e.g., 0.0-1.0);
121, # FC field capacity, i.e., max soil moisture storage [mm] (e.g., 0-600 mm);
2.52, # BETA the non linear parameter for runoff production [-] (e.g., 0.0-20.0);
0.473, # k0 storage coefficient for very fast response [timestep] (e.g., 0.0-2.0 days);
9.06, # k1 storage coefficient for fast response [timestep] (e.g., 2.0-30.0 days);
142, # k2 storage coefficient for slow response [timestep] (e.g., 30.0-250.0 days);
50.1, # lsuz threshold storage state, i.e., the very fast response start if exceeded [mm] (e.g., 1.0-100.0 mm);
2.38, # cperc constant percolation rate [mm/timestep] (e.g., 0.0-8.0 mm/day);
10, # bmax maximum base at low flows [timestep] (e.g., 0.0-30.0 days);
25 # croute free scaling parameter [timestep^2/mm] (e.g., 0.0-50.0 days^2/mm);
)
# set time period
model_in <- indata %>%
filter(date >= as_date("2017-10-01") & date <= as_date("2022-09-30"))
# set up the model
## THIS IS THE ACTUAL MODEL EXECUTION
modelRun <- TUWmodel(
prec = model_in$weighted_precip.mm, # precip input
airt = model_in$Tmean_c, # air temp input
ep = model_in$PET_mm, # pet input
area = 1, # one zone for the entire watershed
param = model_params # input model parameters
)
# get all outputs into a nice df
HBVRun <- tibble(
Date = model_in$Date, # date
P_mm = modelRun$prec, # precip
Tair = modelRun$airt, # air temp
SWEobs = model_in$weighted_swe.mm, # observed swe
PET = modelRun$ep,# pet
Qobs = model_in$Q_mm_day, # observed discharge
Qsim = modelRun$q[1, ], # simulated discharge
Qsurf = modelRun$q0[1, ], # surface runoff
Qsubsurf = modelRun$q1[1, ], # subsurface flow
Qbase = modelRun$q2[1, ], # groundwater flow
Rain = modelRun$rain[1, ], # simulated rain
Snow = modelRun$snow[1, ], # simulated snowfall
Melt = modelRun$melt[1, ], # simulated melt
SWEsim = modelRun$swe[1, ], # simulated swe
Soilmoist = modelRun$moist[1, ], # simulated soil storage
AET = modelRun$eta[1, ], # simulated evapotranspiration
StorageUpper = modelRun$suz[1, ], # upper storage value
StorageLower = modelRun$slz[1, ] # lower storage value
)
#Trim out the warm up period
OutTrim <- HBVRun %>% slice(366:n())
#Calculate NSE
NSE <- 1 - ((sum((OutTrim$Qsim - OutTrim$Qobs) ^ 2)) /
sum((OutTrim$Qobs - mean(OutTrim$Qobs)) ^ 2))
print(NSE)
[1] -0.3496303
Plot observed and model simulations
Generate plots that include and compare the different modeled fluxes
from your best NSE. Some of those fluxes can be immediately compared to
observed data (e.g., runoff or SWE), while others only exist in
simulated form (e.g., storages or outflows of the various runoff
components) and need to be assessed with the perceptual model in
mind.
Make sure that the axes are properly labeled when you create plots. The
script below will get you started.
# Add date to the HBVRun result tibble
HBVRun <- as.data.frame(HBVRun)
HBVRun$Date <- model_in$date
str(HBVRun)
'data.frame': 1826 obs. of 18 variables:
$ P_mm : num 26.78 7.65 1.91 0 0 ...
$ Tair : num 0.8 -1.6 -0.7 6.2 6.65 0.25 3.2 2.5 -7.8 -1.8 ...
$ SWEobs : num 13.3 46.7 53.4 53.4 51.2 ...
$ PET : num 1.3 1.15 1.23 1.84 1.76 ...
$ Qobs : num 0.282 0.243 0.234 0.23 0.231 ...
$ Qsim : num 0.0135 0.0418 0.0661 0.0531 0.0398 ...
$ Qsurf : num 0 0 0 0 0 0 0 0 0 0 ...
$ Qsubsurf : num 0.1343 0 0 0 0.0421 ...
$ Qbase : num 0.0342 0.0434 0.0431 0.0569 0.0732 ...
$ Rain : num 10.7 0 0 0 0 ...
$ Snow : num 16.07 7.65 1.91 0 0 ...
$ Melt : num 2.04 0 0 11.76 12.57 ...
$ SWEsim : num 14.825 22.858 24.866 13.101 0.526 ...
$ Soilmoist : num 60.1 60.1 60.1 68 75.9 ...
$ AET : num 1.3 0 0 1.84 1.76 ...
$ StorageUpper: num 1.36 0 0 0 0.52 ...
$ StorageLower: num 4.85 6.16 6.12 8.08 10.39 ...
$ Date : Date, format: "2017-10-01" ...
# Round the NSE for display
nse_label <- paste("NSE =", round(NSE, 3))
q_plot <- ggplot(data = HBVRun) +
geom_line(aes(x = Date, y = Qobs, color = "Qobs")) + # Qobs
geom_line(aes(x = Date, y = Qsim, color = "Qsim")) + # Qsim
annotate("text",
x = max(HBVRun$Date) - 100, # Move label # days from the end
y = max(HBVRun$Qobs, na.rm = TRUE),
label = nse_label,
hjust = 1, vjust = 1.5, size = 5, fontface = "bold") +
labs(x = NULL, y = "Q (mm/day)", color = NULL, title = "Qobs and Qsim")
# Make it interactive with plotly
ggplotly(q_plot)
NA
More plots
# Swe
swe_plot <- ggplot(data = HBVRun) +
geom_line(aes(x = Date, y = SWEobs, color = "SWEobs")) + # SWEobs
geom_line(aes(BLANK)) + # SWEsim
labs(x = {}, y = "SWE (mm)", color = {})
ggplotly(swe_plot)
# Q0, Q1, Q2
q_bucket_plot <- ggplot(data = HBVRun) +
geom_line(aes(x = Date, y = Qsurf, color = "Qsurf")) + # Qsurf
geom_line(aes(x = Date, y = Qsubsurf, color = "Qsubsurf")) + #Qsubsurf
geom_line(aes(x = Date, y = Qbase, color = "Qbase")) + # Qbase
labs(x = {}, y = "Q (mm)", color = {}, title = "Q0, Q1, and Q2")
ggplotly(q_bucket_plot)
LS0tCnRpdGxlOiAiSEJWIE1vZGVsIgphdXRob3I6ICJZT1VSIE5BTUUgSEVSRSIKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKCiNMb2FkIHBhY2thZ2VzCiMgcGtnVGVzdCBpcyBhIGhlbHBlciBmdW5jdGlvbiB0byBsb2FkIHBhY2thZ2VzIGFuZCBpbnN0YWxsIHBhY2thZ2VzIG9ubHkgd2hlbiB0aGV5IGFyZSBub3QgaW5zdGFsbGVkIHlldC4KCnBrZ1Rlc3QgPC0gZnVuY3Rpb24oeCkKewogIGlmICh4ICVpbiUgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpID09IEZBTFNFKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKHgsIGRlcGVuZGVuY2llcz0gVFJVRSkKICB9CiAgbGlicmFyeSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCn0KbmVlZGVkUGFja2FnZXMgPC0gYygidGlkeXZlcnNlIiwgImx1YnJpZGF0ZSIsICJicm9vbSIsICJUVVdtb2RlbCIsICJwbG90bHkiKSAKCmZvciAocGFja2FnZSBpbiBuZWVkZWRQYWNrYWdlcyl7cGtnVGVzdChwYWNrYWdlKX0KYGBgCgpJbiB0aGlzIGV4ZXJjaXNlLCB3ZSB3aWxsIHB1bGwgdG9nZXRoZXIgbWFueSBhc3BlY3RzIG9mIHdoYXQgd2UgaGF2ZSBsZWFybmVkIHNvIGZhciB0byBjYWxpYnJhdGUgdGhlIEhCViBtb2RlbCB0byB3ZWlnaHRlZCBkYXRhIGZyb20gdGhlIEZyYXNlciBFeHBlcmltZW50YWwgRm9yZXN0IGluIENvbG9yYWRvIHVzaW5nIHByZWNpcGl0YXRpb24vcnVub2ZmIGFuZCBwb3RlbnRpYWwgZXZhcG90cmFuc3BpcmF0aW9uIChQRVQpIGRhdGEgZGVyaXZlZCBmcm9tIHRoZSBtZXRob2RzIGV4cGxvcmVkIGluIHRoZSBzbm93bWVsdCBhbmQgZXZhcG90cmFuc3BpcmF0aW9uIG1vZHVsZXMuIAoKSEJWIGlzOgpJbiB0aGUgcHJldmlvdXMgbGFicywgd2UgaGF2ZSB3b3JrZWQgd2l0aCBzaW5nbGUtcHJvY2VzcyBtb2RlbHMuIEhCViBpcyBtb3JlIGNvbXBsZXggcHJvY2Vzcy1iYXNlZCBtb2RlbCB1c2luZyBtYW55IHBhcmFtZXRlcnMgdG8gZGVzY3JpYmUgdGhlIHBoeXNpY2FsIHByb2Nlc3NlcyBvZiB0aGUgaHlkcm9sb2dpYyBjeWNsZQpodHRwczovL3JwdWJzLmNvbS9zZGhha2FsLzg0ODIzNgoKIVtdKGltYWdlcy9IQlYtc2NoZW0tU2hyZXN0aGEtU29sb21hbnRpbmUtMjAwOC5wbmcgIkhCViBNb2RlbCIpCgpGcm9tOiBEdXJnYSBMYWwgU2hyZXN0aGEgJiBEaW1pdHJpIFAuIFNvbG9tYXRpbmUgKDIwMDgpIERhdGHigJBkcml2ZW4gYXBwcm9hY2hlcyBmb3IgZXN0aW1hdGluZyB1bmNlcnRhaW50eSBpbiByYWluZmFsbOKAkHJ1bm9mZiBtb2RlbGxpbmcsIEludGVybmF0aW9uYWwgSm91cm5hbCBvZiBSaXZlciBCYXNpbiBNYW5hZ2VtZW50LCA2OjIsIDEwOS0xMjIsIERPSTogMTAuMTA4MC8xNTcxNTEyNC4yMDA4Ljk2MzUzNDEKCkhCViB2ZXJzaW9ucyB3aXRoIGEgR1VJIGV4aXN0cywgYnV0IHRoZSBkYXRhIG1hbmlwdWxhdGlvbiBpcyBtb3JlIGNvbnZlbmllbnQgaW4gYSBjb2RpbmcgZW52aXJvbm1lbnQsIGFuZCB3ZSB3YW50IHlvdSB0byBiZSBhYmxlIHRvIHNlZSAndW5kZXIgdGhlIGhvb2QnLiBJbnN0ZWFkIG9mIGRlYWxpbmcgd2l0aCB0aGUgaW5uZXIgd29ya2luZ3Mgb2YgdGhlIG1vZGVsIHRvbyBtdWNoLCB3ZSB3aWxsIHB1dCB0aGUgZm9jdXMgb24gaW1wb3J0aW5nIGFuZCBvcmdhbml6aW5nIHRoZSBkYXRhLCBnZXR0aW5nIHRoZSBtb2RlbCBydW5uaW5nLCBtYW51YWwgb3B0aW1pemF0aW9uLiAgCgpUaGUgSEJWIHJlYWxpemF0aW9uIHdlIHdpbGwgYmUgdXNpbmcgaXMgY29udGFpbmVkIGluIHRoZSBSIHBhY2thZ2UgVFVXbW9kZWwuIFBsZWFzZSBzZWFyY2ggdGhhdCBwYWNrYWdlIGluIHlvdXIgYnJvd3NlciBhbmQgZmFtaWxpYXJpemUgeW91cnNlbGYgd2l0aCBpdCAodGhhdCB3aWxsIGJlIHRoZSBmaXJzdCBzdGVwKS4gVFVXIHNpbXBseSBtZWFucyAiVGVjaG5pc2NoZSBVbml2ZXJzaXRhZXQgV2llbiIsIG9yIFZpZW5uYSBVbml2ZXJzaXR5IG9mIFRlY2hub2xvZ3kuIFRoZSBtb2RlbCBpcyBzbGlnaHRseSBkaWZmZXJlbnQgZnJvbSB0aGUgb3JpZ2luYWwgSEJWIGJ1dCBzdGlsbCBzaW1pbGFyIGVub3VnaCB0byBjYWxsIGl0IEhCVi4gV2UgYWxzbyBuZWVkIHRvIHVuZGVyc3RhbmQgdGhlIGFyZ3VtZW50cyBhbmQgcGFyYW1ldGVycyByZXF1aXJlZCBmb3IgdGhlIG1vZGVsLiAKClR5cGUgJz9UVVdtb2RlbCcgaW4geW91ciBjb25zb2xlLiBUaGUgcGFja2FnZSBjb21lcyB3aXRoIGFuIGV4YW1wbGUgZGF0YXNldCwgYW5kIHlvdSBjYW4gcnVuIGV4YW1wbGVzIGVpdGhlciBieSBjb3B5aW5nIGFuZCBwYXN0aW5nIHRoZSBkYXRhKCksIFRVV21vZGVsKCkgYW5kIHBsb3RzIGludG8geW91ciBjb25zb2xlLCBvciB5b3UgY2FuIGZpbmQgYW5kIGNsaWNrICdSdW4gZXhhbXBsZXMnIHVuZGVyIHRoZSAqKkV4YW1wbGVzKiogaGVhZGVyIGFuZCB2aWV3IHRoZSBzY3JpcHQgYW5kIHBsb3Qgb3V0cHV0cyBpbiB0aGUgSGVscCB3aW5kb3cuIFRoZSBmaXJzdCBleGFtcGxlIHNob3dzIGhvdyB0aGUgICdUVVdtb2RlbCcgY2FuIGJlIHJ1biBnaXZlbiBzcGVjaWZpY2F0aW9ucyBmb3IgYSBsb25nIHNldCBvZiBwYXJhbWV0ZXJzIChwYXJhbSkuIFlvdSBjYW4gcHJvdmlkZSB0aGUgbW9kZWwgd2l0aCBwcmVjaXAsIGFpciB0ZW1wZXJhdHVyZSwgYXJlYSBhbmQgRVQgYWxvbmcgd2l0aCB0aGUgcGFyYW1ldGVyIHNldCBhbmQgaXQgd2lsbCBzaW11bGF0ZSBTV0UsIHNub3dtZWx0IGFuZCBkaXNjaGFyZ2UuIFdlIGNhbiBvYnRhaW4gbWVhc3VyZW1lbnRzIGZvciBlYWNoIG9mIHRoZXNlIHZhcmlhYmxlcywgc28gd2Ugd2lsbCBpbXBvcnQgYWxsIG9mIHRoaXMgZGF0YSBhbmQgdGVzdCB0aGUgZml0IG9mIHRoZSBzaW11bGF0ZWQgZGF0YS4gCgpodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvVFVXbW9kZWwvVFVXbW9kZWwucGRmICAKCiMjIyBJbXBvcnQgZGF0YTogCldlIHdpbGwgZ2VuZXJhdGUgYSBjb25jZXB0dWFsIHJ1bm9mZiBtb2RlbCBzdGFydGluZyB3aXRoOjxicj4KLSBSYWluTWVsdFdlaWdodGVkX21tIC0tPiBsaXF1aWQgaW5wdXQgdG8gdGhlIGdyb3VuZCwgY29tYmluYXRpb24gb2YgcmFpbiBhbmQgbWVsdDxicj4KLSBQV2VpZ2h0ZWRfbW0gLS0+IFByZWNpcGl0YXRpb24gbWVhc3VyZWQgYXQgdGhlIFNOT1RFTCBzaXRlcywgYm90aCBsaXF1aWQgYW5kIGZyb3plbi4gVGhpcyBpcyBOT1Qgd2F0ZXIgdGhhdCB3ZW50IGludG8gdGhlIGdyb3VuZCBhdCB0aGF0IHBvaW50LiBUaGlzIGlzLCBob3dldmVyLCB0aGUgUCBpbnB1dCB0aW1lIHNlcmllcyBmb3IgdGhlIEhCViBtb2RlbC48YnI+Ci0gU3dlV2VpZ2h0ZWRfbW0gLS0+IFNub3cgd2F0ZXIgZXF1aXZhbGVudCBhdCB0aGUgU05PVEVMIHNpdGVzLiBXZSBjYW4gdXNlIHRoaXMgdG8gZXZhbHVhdGUgb3VyIHNub3cgcm91dGluZS48YnI+Ci0gVGVtcFdlaWdodGVkIC0tPiBFaXRoZXIgZGFpbHkgbWVhbiwgbWluLCBvciBtYXguIFdlIHdpbGwgdXNlIHRoZSBtZWFuLgotIERpc2NoYXJnZV9tbSAtLT4gUSBvYnNlcnZlZCBhdCB0aGUgb3V0bGV0Ljxicj4KLSBFVCAtLT4gUG90ZW50aWFsIEV2YXBvdHJhbnNwaXJhdGlvbiBjYWxjdWxhdGVkIHVzaW5nIHJlbGF0aXZlIGh1bWlkaXR5IGFuZCB0ZW1wZXJhdHVyZS48YnI+CgojIyMgUmVwbyBsaW5rCltEb3dubG9hZCB0aGUgcmVwbyBmb3IgdGhpcyBsYWIgSEVSRV0oaHR0cHM6Ly9naXRodWIuY29tL3RwY292aW5vLzA4X2hidl9tb2RlbHMtLmdpdCl7dGFyZ2V0PSJfYmxhbmsifQoKIyMjIExhYndvcmsgKDIwIHBudHMpCgpPdXIgZmlyc3Qgc3RlcHMgd2lsbCBiZSB0byBpbXBvcnQgYW5kIGZvcm1hdCB0aGUgZGF0YS4gQXMgeW91IG1pZ2h0IHJlY2FsbCBmcm9tIHRoZSBwcmV2aW91cyBtb2R1bGVzLCB0aGlzIHN0ZXAgY2FuIHJlcXVpcmUgdGhlIG1vc3Qgc2NyaXB0IGFuZCAnd29yaycsIGJ1dCBpcyBjcml0aWNhbCB0byBhIHZhbGlkIG1vZGVsIG91dHB1dC4gSW4gdGhpcyBhc3NpZ25tZW50LCB3ZSBoYXZlIHNpbXBsaWZpZWQgbXVjaCBvZiB0aGUgZGF0YSBjb2xsZWN0aW9uIGZvciB5b3UsIGFzIHRoZSBzdGVwcyBhcmUgb25lcyB5b3UgaGF2ZSBhbHJlYWR5IGNvbXBsZXRlZCBpbiBwcmV2aW91cyBtb2R1bGVzLiAKSW4gdGhlIC5jc3YgaW1wb3J0ZWQgYmVsb3csIHdlIHN0YXJ0ZWQgYnkgY29sbGVjdGluZyB0aGUgc2FtZSBTTk9URUwgZGF0YSB0aGF0IHlvdSB1c2VkIGluIHRoZSBzbm93bWVsdCBtb2R1bGUsIGJ1dCBkb3dubG9hZGVkIGZvciBhIGdyZWF0ZXIgZGF0ZSByYW5nZSAod2F0ZXIgeWVhcnMgMjAxOC0yMDIyKSAoZGF0ZSwgU1dFIGRlcHRoIGluIG1tLCBhbmQgZGFpbHkgcHJlY2lwaXRhdGlvbiAobW0pKSBIb3dldmVyLCB5b3Ugd2lsbCBub3RpY2UgdGhhdCB0aGUgY29sdW1uIG5hbWVzIG9mIHRoZSBwcm92aWRlZCB0YWJsZSBjb250YWluICd3ZWlnaHRlZF8nLiAKCmBgYHtyfQojIEltcG9ydCBkYXRhCmluZGF0YSA8LSByZWFkLmNzdigiSEJWX2RhdGEvd2VpZ2h0ZWRfZGF0YV9mb29sX0hCVi5jc3YiKSU+JQogIG11dGF0ZShkYXRlID0geW1kKGRhdGUpKSAlPiUKICByZW5hbWUoUV9tM19zID0gbTNfcywgUV9tbV9kYXkgPSBtbV9kYXksIHJ1bm9mZl9pbnB1dC5tbSA9IGlucHV0Lm1tICkKCnN0cihpbmRhdGEpCmBgYAoKQXMgd2Ugbm90ZWQgaW4gdGhlIFNub3dtZWx0IG1vZHVsZSwgZmFjdG9ycyBzdWNoIGFzIGVsZXZhdGlvbiBhbmQgdmVnZXRhdGlvbiB3aWxsIGFmZmVjdCB0aGUgb2Ygc25vdy10by1ydW5vZmYgcmF0ZS4gU2ltaWxhcmx5LCB0ZW1wZXJhdHVyZSBhbmQgcHJlY2lwaXRhdGlvbiBkaWZmZXIgd2l0aCBlbGV2YXRpb24sIG1lYW5pbmcgdGhhdCBTTk9URUwgZGF0YSBmcm9tIGEgc2luZ2xlIGxvY2F0aW9uIG1heSBub3QgZnVsbHkgcmVwcmVzZW50IGNvbmRpdGlvbnMgYWNyb3NzIHRoZSBlbnRpcmUgd2F0ZXJzaGVkLgoKVG8gYWNjb3VudCBmb3IgdGhlc2UgdmFyaWF0aW9ucywgdGhlICJ3ZWlnaHRlZCIgdmFsdWVzIGZvciB0aGlzIGV4ZXJjaXNlIGhhdmUgYmVlbiBhZGp1c3RlZCB1c2luZyBsaW5lYXIgc2NhbGluZyByZWxhdGlvbnNoaXBzIHRoYXQgZXN0aW1hdGUgYXZlcmFnZSBjb25kaXRpb25zIGFjcm9zcyB0aGUgd2F0ZXJzaGVkLiBUaGUgYWRqdXN0bWVudHMgdXNlIGVsZXZhdGlvbi1wcmVjaXBpdGF0aW9uIGFuZCBlbGV2YXRpb24tdGVtcGVyYXR1cmUgcmVsYXRpb25zaGlwcyBkZXJpdmVkIGZyb20gaW52ZXJzZSBkaXN0YW5jZSB3ZWlnaHRpbmcgdG8gYmV0dGVyIGFwcHJveGltYXRlIHNwYXRpYWxseSBkaXN0cmlidXRlZCBoeWRyb2xvZ2ljYWwgaW5wdXRzIHJhdGhlciB0aGFuIHJlbHlpbmcgb24gYSBzaW5nbGUgcG9pbnQgbWVhc3VyZW1lbnQuCgoKIVtdKGltYWdlcy9wcmVjaXBfc3BhdGlhbF9pbnRlcnAucG5nICJTcGF0aWFsIEludGVycG9sYXRpb24iKQoKCklmIHlvdSBhcmUgaW50ZXJlc3RlZCwgeW91IGNhbiBzdGFydCBleHBsb3Jpbmcgc3BhdGlhbCBpbnRlcnBvbGF0aW9uIHdpdGg6CjEuIFRoaWVzc2VuIHBvbHlnb25zCiFbVGhpZXNzZW4gcG9seWdvbnNdKGh0dHBzOi8vdXBsb2FkLndpa2ltZWRpYS5vcmcvd2lraXBlZGlhL2NvbW1vbnMvdGh1bWIvZC9kOS9Wb3Jvbm9pX2dyb3d0aF9ldWNsaWRlYW4uZ2lmLzQ0MHB4LVZvcm9ub2lfZ3Jvd3RoX2V1Y2xpZGVhbi5naWYpCjIuIEludmVyc2UgZGlzdGFuY2Ugd2VpZ2h0aW5nCjMuIEtyaW5naW5nIG1ldGhvZHMKdG8gZGV0ZXJtaW5lIGlmIGFueSBvZiB0aGVzZSBhcmUgYXBwbGljYWJsZSB0byB5b3VyIHN0dWR5IGFyZWEuCgpPdGhlciBjb2x1bW5zIGluIHRoaXMgZGF0YSBpbmNsdWRlOgoqKlFfbTNfcyBhbmQgUV9tbV9kYXkqKiAtIERpc2NoYXJnZSAoUSkgY29sbGVjdGVkIGF0IHRoZSBGb29sIENyZWVrIG91dGxldCBieSBVU0ZTLCBpbiB1bml0cyBvZiBjdWJpYyBtZXRlcnMgcGVyIHNlY29uZCBhbmQgbW0gcGVyIGRheSBmcm9tIEFwcmlsIHVudGlsIE9jdG9iZXIuCioqVG1heF9jLCBUbWluX2MgYW5kIFRtZWFuX2MqKiAtIFR5cGljYWxseSwgd2UgY291bGQgZmluZCBkYWlseSBtZWFuLCBtYXggYW5kIG1pbmltdW0gdGVtcGVyYXR1cmVzIGluIHRoZSBTTk9URUwgZGF0YXNldHMsIGhvd2V2ZXIsIHRoaXMgcGFydGljdWxhciBzdGF0aW9uIGlzIG1pc3NpbmcgdGVtcGVyYXR1cmUgZGF0YSAoZHVlIHRvIHJlc3RyaWN0aW9ucyBvbiB0ZWNobmljYWwgYWNjZXNzIGluIDIwMjApLCBzbyB3ZSByZXRyaWV2ZWQgdGVtcGVyYXR1cmUgZGF0YSBmcm9tIEdyaWRNRVQgdGhyb3VnaCBbQ2xpbWF0ZSBFbmdpbmVdKGh0dHBzOi8vYXBwLmNsaW1hdGVlbmdpbmUub3JnL2NsaW1hdGVFbmdpbmUpLiBUaGlzIGhpZ2hsaWdodHMgdGhlIGltcG9ydGFuY2Ugb2YgZXZhbHVhdGluZyBlYWNoIHZhcmlhYmxlIGZvciBjb21wbGV0ZW5lc3MuIEl0IHdpbGwgc2F2ZSB5b3UgdGhlIGhlYWRhY2hlIG9mIHJ1bm5pbmcgdGhlIGVudGlyZSB3b3JrZmxvdywgb25seSB0byBmaW5kIHRoYXQgbW9kZWwgb3V0cHV0cyBjYW5ub3QgYmUgc2ltdWxhdGVkIGZvciB0aGUgbGF0ZXIgaGFsZiBvZiAyMDIwIGR1ZSB0byBtaXNzaW5nIGlucHV0IGRhdGEuCioqUkhtaW4sIFJIbWF4KiogLSBSZWxhdGl2ZSBodW1pZGl0eSBkYWlseSBtaW4gYW5kIG1heCwgYWxzbyBmcm9tIEdyaWRNRVQsIGFjY2Vzc2VkIHRocm91Z2ggQ2xpbWF0ZSBFbmdpbmUKKipTV0VkaWZmLm1tLCBQZGlmZi5tbSBhbmQgcnVub2ZmX2lucHV0Lm1tKiogLSBhbGwgZGFpbHkgb3V0cHV0cyBvZiBhIHRlbXBlcmF0dXJlIGJhc2VkIHNub3dtZWx0IG1vZGVsICh0aGUgc2FtZSBhcyBpbiB0aGUgc25vd21lbHQgbW9kdWxlKS4gcnVub2ZmX2lucHV0IGlzIHRoZSBlc3RpbWF0ZWQgZGFpbHkgaW5wdXQgdG8gdGhlIHN0cmVhbSBmcm9tIG1lbHRlZCBzbm93IGFuZCBsaXF1aWQgcHJlY2lwaXRhdGlvbiBjb21iaW5lZC4KCktlZXAgaW4gbWluZCB0aGF0IHlvdSBjYW4gcGxvdCBtb3JlIHRoYW4gdHdvIHZhcmlhYmxlcyBpbiBhIHNpbmdsZSBwbG90LCBidXQgdGhleSB3aWxsIGJlIG1vc3QgaGVscGZ1bCBpZiB5b3UgZ3JvdXAgdmFyaWFibGVzIHdpdGggYSBzaW1pbGFyIHktc2NhbGUuIEZvciBleGFtcGxlLCBjdW11bGF0aXZlIHByZWNpcGl0YXRpb24gb3IgU1dFIHZhbHVlcyB3aWxsIGhhdmUgYSBkaWZmZXJlbnQgcmFuZ2UgdGhhbiB2YXJpYWJsZXMgdGhhdCByZXByZXNlbnQgZGFpbHkgbWVhc3VyZW1lbnRzIGxpa2UgJ2lucHV0X21tJy4gQWx0ZXJuYXRpdmVseSwgeW91IGNhbiBhZGQgYSBzZWNvbmRhcnkgeS1heGlzIHRvIHlvdXIgcGxvdC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGluZGF0YSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkYXRlLCB5ID0gcnVub2ZmX2lucHV0Lm1tLCBjb2xvciA9ICJydW5vZmZfaW5wdXQubW0iKSwgc2l6ZSA9IDAuNSkgKyAgIyBBc3NpZ24gYSBsYWJlbCBmb3IgbGVnZW5kCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRhdGUsIHkgPSBRX21tX2RheSwgY29sb3IgPSAibW0vZGF5IiksIHNpemUgPSAwLjUpICsgICMgQXNzaWduIGEgZGlmZmVyZW50IGxhYmVsCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRhdGUsIHkgPSB3ZWlnaHRlZF9wcmVjaXAubW0sIGNvbG9yID0gIndlaWdodGVkX3ByZWNpcC5tbSIpLCBzaXplID0gMC41KSArICAjIEFzc2lnbiBhIGRpZmZlcmVudCBsYWJlbAogIGdlb21fcG9pbnQoYWVzKHggPSBkYXRlLCB5ID0gUGRpZmYubW0sIGNvbG9yID0gIlBkaWZmLm1tIiksIHNpemUgPSAwLjUpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicnVub2ZmX2lucHV0Lm1tIiA9ICJibHVlIiwgIm1tL2RheSIgPSAicmVkIiwgIndlaWdodGVkX3ByZWNpcC5tbSIgPSAiZ3JlZW4iLCAiUGRpZmYubW0iID0gJ3B1cnBsZScpKSArICAjIEN1c3RvbWl6ZSBjb2xvcnMKICBsYWJzKGNvbG9yID0gIkRpc2NoYXJnZSBVbml0cyIpICsgICMgTGVnZW5kIHRpdGxlCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIDIsOTMwbS4Kd2F0ZXJzaGVkX2FyZWFfbTIgPC0gMjY0MDAwMCAKCk5vdGUgdGhhdCB3aGlsZSB0aGUgc3RyZWFtIGlzIHNub3ctY292ZXJlZCwgdGhlcmUgYXJlIG5vIHN0YWdlL2Zsb3cgbWVhc3VyZW1lbnRzIGJlaW5nIG1hZGUgYXQgdGhpcyBzaXRlLiBGb3IgbWFueSBvZiBvdXIgY2FsY3VsYXRpb25zIHRvIHdvcmssIHdlIHdpbGwgbm90IHdhbnQgTkEgaW4gb3VyIGRhdGEgZnJhbWVzLiBJbiB0aGlzIGRhdGEgc2V0LCBpZiB3ZSB2aWV3IHRoZSB0YWJ1bGFyIGRhdGEsIHRoZSBlbmQgZGlzY2hhcmdlIHJlYWRpbmcgKFFfbW1fZGF5KSwgaXMgdmVyeSBzaW1pbGFyIHRvIHRoZSBmaXJzdCBpbiB0aGUgZm9sbG93aW5nIGNhbGVuZGFyIHllYXIuIEZvciB0aGlzIGV4YW1wbGUsIHdlIHdpbGwgdGhlbiBmaWxsIHRoZSBOQSB2YWx1ZXMgd2l0aCB0aGUgbWVhbiBvZiB0aGUgZmluYWwgYW5kIGZpcnN0IHJlYWRpbmdzIGZvciBlYWNoIHdpbnRlciAoT2N0IC0gQXByaWwpIE5BIHN0cmluZy4KCmBgYHtyfQojIFJhdGhlciB0aGFuIHNjcm9sbGluZyB0aHJvdWdoIGEgZGF0YWZyYW1lLCB0aGlzIGdpdmVzIHVzIGEgc3VtIG9mICdOQScgcm9zIGluIHRoaXMgY29sdW1uCnN1bShpcy5uYShpbmRhdGEkUV9tbV9kYXkpKQpgYGAKCmBgYHtyfQojIEZ1bmN0aW9uIHRvIGZpbGwgTkEgdmFsdWVzIGluIFFfbW1fZGF5IGR1cmluZyB3aW50ZXIgKE9jdCAtIEFwcikKZmlsbF9uYV93aW50ZXIgPC0gZnVuY3Rpb24oZGYpIHsKICBkZiA8LSBkZiAlPiUKICAgIGFycmFuZ2UoZGF0ZSkgJT4lICMgRW5zdXJlIGRhdGEgaXMgc29ydGVkCiAgICBtdXRhdGUoCiAgICAgIG1vbnRoID0gbW9udGgoZGF0ZSksCiAgICAgIGlzX3dpbnRlciA9IG1vbnRoICVpbiUgYygxMCwgMTEsIDEyLCAxLCAyLCAzLCA0KSAjIElkZW50aWZ5IHdpbnRlciBtb250aHMKICAgICkKICAKICAjIElkZW50aWZ5IE5BIHN0cmV0Y2hlcyBpbiB3aW50ZXIgbW9udGhzCiAgbmFfaW5kaWNlcyA8LSB3aGljaChpcy5uYShkZiRRX21tX2RheSkgJiBkZiRpc193aW50ZXIpCiAgCiAgZm9yIChpZHggaW4gbmFfaW5kaWNlcykgewogICAgIyBGaW5kIHRoZSBsYXN0IG5vbi1OQSBiZWZvcmUgdGhlIGN1cnJlbnQgTkEKICAgIHByZXZfdmFsdWUgPC0gZGYkUV9tbV9kYXlbbWF4KHdoaWNoKCFpcy5uYShkZiRRX21tX2RheVsxOihpZHggLSAxKV0pKSldCiAgICAKICAgICMgRmluZCB0aGUgbmV4dCBub24tTkEgYWZ0ZXIgdGhlIGN1cnJlbnQgTkEKICAgIG5leHRfdmFsdWUgPC0gZGYkUV9tbV9kYXlbbWluKHdoaWNoKCFpcy5uYShkZiRRX21tX2RheVsoaWR4ICsgMSk6bnJvdyhkZildKSkgKyBpZHgpXQogICAgCiAgICAjIFJlcGxhY2UgdGhlIE5BIHdpdGggdGhlIGF2ZXJhZ2Ugb2YgcHJldl92YWx1ZSBhbmQgbmV4dF92YWx1ZQogICAgaWYgKCFpcy5uYShwcmV2X3ZhbHVlKSAmICFpcy5uYShuZXh0X3ZhbHVlKSkgewogICAgICBkZiRRX21tX2RheVtpZHhdIDwtIChwcmV2X3ZhbHVlICsgbmV4dF92YWx1ZSkgLyAyCiAgICB9CiAgfQogIAogIHJldHVybihkZikKfQoKIyBBcHBseSB0aGUgZnVuY3Rpb24KaW5kYXRhIDwtIGZpbGxfbmFfd2ludGVyKGluZGF0YSkKYGBgCgpMZXQncyB0cnkgb3VyIHF1aWNrIGNoZWNrIGFnYWluOiAKYGBge3J9CiMgTm93IHdlIHNob3VsZCBzZWUgemVybyBOQSBpbiB0aGlzIGNvbHVtbgpzdW0oaXMubmEoaW5kYXRhJFFfbW1fZGF5KSkKYGBgCgpOZXh0LCB3ZSBuZWVkIHRvIGFkZCBkYWlseSBwb3RlbnRpYWwgZXZhcG90cmFuc3BpcmF0aW9uIChQRVQpIHRvIHRoZSBkYXRhc2V0LiBUaGUgZXZhcG90cmFuc3BpcmF0aW9uIG1vZHVsZSBjb3ZlcmVkIHNvbWUgb2YgdGhlIG51bWVyb3VzIHByZS1idWlsdCBmdW5jdGlvbnMgYXZhaWxhYmxlIGluIHZhcmlvdXMgUiBwYWNrYWdlcyBkZXNpZ25lZCBmb3IgRVQgZXN0aW1hdGlvbi4gRm9yIGluc3RhbmNlLCB0aGUgJ0V2YXBvdHJhbnNwaXJhdGlvbicgcGFja2FnZSBwcm92aWRlcyBFVCBlc3RpbWF0ZXMgZGVyaXZlZCBmcm9tIGFwcHJveGltYXRlbHkgMjAgZGlzdGluY3QgZXF1YXRpb25zIG9yIHZhcmlhdGlvbnMuIFRoZXNlIGZ1bmN0aW9ucyByZXF1aXJlIHByZWNpc2UgZGF0YSBmb3JtYXR0aW5nIHRvIGVuc3VyZSBjb21wYXRpYmlsaXR5IHdpdGggdGhlIGZ1bmN0aW9uIGFyZ3VtZW50cy4gVG8gaGVscCB5b3Ugc3RydWN0dXJlIHlvdXIgZnVuY3Rpb24gaW5wdXRzIHNpbWlsYXJseSwgbWFueSBwYWNrYWdlcyBjb21lIHdpdGggZXhhbXBsZSBkYXRhc2V0cyBpbiB0aGVpciBkb2N1bWVudGF0aW9uLiBTUEVJIGlzIGFub3RoZXIgcGFja2FnZSB0aGF0IG9mZmVycyBFVCBmdW5jdGlvbnMuIFRoZSBnaXRodWIgcmVwb3NpdG9yeSBmb3IgdGhpcyBwYWNrYWdlIGhhcyBiZWVuIHVwZGF0ZWQgZmFpcmx5IHJlY2VudGx5LCB3aGljaCBjYW4gYmUgaW1wb3J0YW50IHRvIHZlcmlmeS4gCgpXZSBjaG9zZSB0byB3cml0ZSBvdXIgb3duIGZ1bmN0aW9uIGhlcmUgc28geW91IGNvdWxkICdzZWUgdW5kZXIgdGhlIGhvb2QnLiAgCk5vdCBhbGwgcGFja2FnZXMgYXJlIGVxdWFsbHkgbWFpbnRhaW5lZCwgYXMgdGhleSBhcmUgb2Z0ZW4gZGV2ZWxvcGVkIGJ5IHJlc2VhcmNoZXJzIG9yIG1vZGVsZXJzIHRvIGltcHJvdmUgdGhlIHJlcGVhdGFibHkgb2YgdGhlaXIgd29yaywgYW5kIGNvbnRyaWJ1dGUgdG8gdGhlIGJyb2FkZXIgc2NpZW50aWZpYyBjb21tdW5pdHkuIE9uY2UgZnVuZGluZyBjZWFzZXMsIG9yIGlmIHRoZSBvcmlnaW5hbCBkZXZlbG9wZXIgbW92ZXMgb24gdG8gbmV3IHByb2plY3RzLCBhIGN1c3RvbSBwYWNrYWdlIG1heSBubyBsb25nZXIgcmVjZWl2ZSB1cGRhdGVzIG9yIHN1cHBvcnQuIEFzIFIsIFJTdHVkaW8gYW5kIG90aGVyIHN1cHBvcnRpbmcgcGFja2FnZXMgYXJlIHVwZGF0ZWQsIGEgcGFja2FnZSBtYXkgYmVjb21lIGRlcHJlY2lhdGVkLiBJZiB0aGUgcGFja2FnZSBzdGlsbCBmdW5jdGlvbnMgY29ycmVjdGx5IGluIHlvdXIgY3VycmVudCBSIHZlcnNpb24gYW5kIGRlcGVuZGVuY2llcywgdGhlcmXigJlzIG5vIGltbWVkaWF0ZSByZWFzb24gdG8gc3RvcCB1c2luZyBpdC4gSG93ZXZlciwgaWYgeW91IGFyZSBjb25jZXJuZWQgdGhhdCBmdXR1cmUgdmVyc2lvbnMgb2YgUiBvciBkZXBlbmRlbmNpZXMgbWlnaHQgYnJlYWsgdGhlIHBhY2thZ2VzIHlvdSB1c2UsIHlvdSBjYW4gY2hlY2sgdGhlIGRldmVsb3BtZW50IGFuZCBtYWludGVuYW5jZSBoaXN0b3J5IG9mIGN1c3RvbSBvciBzcGVjaWFsaXplZCBwYWNrYWdlcyAob3Igd3JpdGUgYSBwYWNrYWdlIG9yIGZ1bmN0aW9uIGZvciB5b3Vyc2VsZiBhcyBleGVtcGxpZmllZCBiZWxvdyEpLgoKSGVyZSBpcyBvdXIgSGFyZ3JlYXZlcyBmdW5jdGlvbiwgYWRhcHRlZCBmcm9tIHRoZSAnRXZhcG90cmFuc3BpcmF0aW9uJyBwYWNrYWdlIHRvIG1pbmltaXplIHJlLWZvcm1hdHRpbmcgb2Ygb3VyIGRhdGFmcmFtZS4gCgpgYGB7cn0KCiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIEVUCmNhbGN1bGF0ZV9FVCA8LSBmdW5jdGlvbihkYXRhLCBjb25zdGFudHMsIHRzID0gImRhaWx5IiwgbWVzc2FnZSA9ICJ5ZXMiLCBzYXZlLmNzdiA9ICJubyIsIC4uLikgewogIAogICMgQ2hlY2sgZm9yIHJlcXVpcmVkIGRhdGEKICBpZiAoaXMubnVsbChkYXRhJFRtYXgpIHwgaXMubnVsbChkYXRhJFRtaW4pKSB7CiAgICBzdG9wKCJSZXF1aXJlZCBkYXRhIG1pc3NpbmcgZm9yICdUbWF4JyBhbmQgJ1RtaW4nLCBvciAnVGVtcCciKQogIH0KICAKICAjIEhhcmdyZWF2ZXMtU2FtYW5pIEVUIENhbGN1bGF0aW9uCiAgVGEgPC0gKGRhdGEkVG1heCArIGRhdGEkVG1pbikgLyAyCiAgUCA8LSAxMDEuMyAqICgoMjkzIC0gMC4wMDY1ICogY29uc3RhbnRzJEVsZXYpIC8gMjkzKSBeIDUuMjYKICBkZWx0YSA8LSA0MDk4ICogKDAuNjEwOCAqIGV4cCgoMTcuMjcgKiBUYSkgLyAoVGEgKyAyMzcuMykpKSAvICgoVGEgKyAyMzcuMykgXiAyKQogIGdhbW1hIDwtIDAuMDAxNjMgKiBQIC8gY29uc3RhbnRzJGxhbWJkYQogIGRfcjIgPC0gMSArIDAuMDMzICogY29zKDIgKiBwaSAvIDM2NSAqIGRhdGEkSikKICBkZWx0YTIgPC0gMC40MDkgKiBzaW4oMiAqIHBpIC8gMzY1ICogZGF0YSRKIC0gMS4zOSkKICB3X3MgPC0gYWNvcygtdGFuKGNvbnN0YW50cyRsYXRfcmFkKSAqIHRhbihkZWx0YTIpKQogIE4gPC0gMjQgLyBwaSAqIHdfcwogIFJfYSA8LSAoMTQ0MCAvIHBpKSAqIGRfcjIgKiBjb25zdGFudHMkR3NjICogKHdfcyAqIHNpbihjb25zdGFudHMkbGF0X3JhZCkgKiBzaW4oZGVsdGEyKSArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcyhjb25zdGFudHMkbGF0X3JhZCkgKiBjb3MoZGVsdGEyKSAqIHNpbih3X3MpKQogIENfSFMgPC0gMC4wMDE4NSAqIChkYXRhJFRtYXggLSBkYXRhJFRtaW4pIF4gMiAtIDAuMDQzMyAqIChkYXRhJFRtYXggLSBkYXRhJFRtaW4pICsgMC40MDIzCiAgRVRfSFMuRGFpbHkgPC0gMC4wMTM1ICogQ19IUyAqIFJfYSAvIGNvbnN0YW50cyRsYW1iZGEgKiAoZGF0YSRUbWF4IC0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSRUbWluKSBeIDAuNSAqIChUYSArIDE3LjgpCiAgRVQuRGFpbHkgPC0gRVRfSFMuRGFpbHkKICAKICAjIENyZWF0ZSBZZWFyTW9udGggQ29sdW1uCiAgZGF0YSRZZWFyTW9udGggPC0gYXMuRGF0ZShwYXN0ZSh5ZWFyKGRhdGEkRGF0ZS5kYWlseSksIG1vbnRoKGRhdGEkRGF0ZS5kYWlseSksICIwMSIsIHNlcCA9ICItIikpCiAgCiAgIyBBbm51YWwgYW5kIE1vbnRobHkgQWdncmVnYXRpb25zCiAgRVQuQW5udWFsIDwtIGFnZ3JlZ2F0ZShFVC5EYWlseSB+IHllYXIoWWVhck1vbnRoKSwgZGF0YSA9IGRhdGEsIEZVTiA9IHN1bSkKICBFVC5Nb250aGx5IDwtIGFnZ3JlZ2F0ZShFVC5EYWlseSB+IFllYXJNb250aCwgZGF0YSA9IGRhdGEsIEZVTiA9IHN1bSkKICAKICAjIEVUIGZvcm11bGF0aW5nCiAgRVRfZm9ybXVsYXRpb24gPC0gIkhhcmdyZWF2ZXMtU2FtYW5pIgogIEVUX3R5cGUgPC0gIlJlZmVyZW5jZSBDcm9wIEVUIgogIHJlc3VsdHMgPC0gbGlzdChFVC5EYWlseSA9IEVULkRhaWx5LCBFVC5Nb250aGx5ID0gRVQuTW9udGhseSwgCiAgICAgICAgICAgICAgICAgIEVULkFubnVhbCA9IEVULkFubnVhbCwgRVRfZm9ybXVsYXRpb24gPSBFVF9mb3JtdWxhdGlvbiwgCiAgICAgICAgICAgICAgICAgIEVUX3R5cGUgPSBFVF90eXBlKQogIAogICMgU2F2ZSB0byBDU1YgaWYgcmVxdWlyZWQKICBpZiAoc2F2ZS5jc3YgPT0gInllcyIpIHsKICAgIGZvciAoaSBpbiAxOmxlbmd0aChyZXN1bHRzKSkgewogICAgICBuYW1lciA8LSBuYW1lcyhyZXN1bHRzW2ldKQogICAgICB3cml0ZS50YWJsZShhcy5jaGFyYWN0ZXIobmFtZXIpLCBmaWxlID0gIkVUX0hhcmdyZWF2ZXNTYW1hbmkuY3N2IiwgCiAgICAgICAgICAgICAgICAgIGRlYyA9ICIuIiwgcXVvdGUgPSBGQUxTRSwgY29sLm5hbWVzID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSBGLCBhcHBlbmQgPSBUUlVFLCBzZXAgPSAiLCIpCiAgICAgIHdyaXRlLnRhYmxlKGRhdGEuZnJhbWUoZ2V0KG5hbWVyLCByZXN1bHRzKSksIGZpbGUgPSAiRVRfSGFyZ3JlYXZlc1NhbWFuaS5jc3YiLCAKICAgICAgICAgICAgICAgICAgY29sLm5hbWVzID0gRiwgYXBwZW5kID0gVFJVRSwgc2VwID0gIiwiKQogICAgfQogICAgaW52aXNpYmxlKHJlc3VsdHMpCiAgfSBlbHNlIHsKICAgIHJldHVybihyZXN1bHRzKQogIH0KfQpgYGAKCk5vdyB3ZSB3aWxsIGZvcm1hdCB0aGUgaW5wdXRzIHNvIHRoZSBkYXRhIGlzIGVhc2lseSByZWFkIGJ5IHRoZSBmdW5jdGlvbiwgYW5kIHJ1biB0aGUgZnVuY3Rpb24uCgpgYGB7cn0KIyBGb3JtYXQgb3VyIGRhdGEgdG8gZml0IHRoZSBmdW5jdGlvbgpQRVRfZGF0YSA8LSBsaXN0KAogIFRtYXggPSBpbmRhdGEkVG1heCwKICBUbWluID0gaW5kYXRhJFRtaW4sCiAgSiA9IGFzLm51bWVyaWMoZm9ybWF0KGluZGF0YSRkYXRlLCAiJWoiKSksCiAgRGF0ZS5kYWlseSA9IGluZGF0YSRkYXRlCikKCiMgRGVmaW5lIGNvbnN0YW50cwpjb25zdGFudHMgPC0gbGlzdCgKICBFbGV2ID0gMjkwMCwgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBFbGV2YXRpb24gaW4gbWV0ZXJzCiAgbGFtYmRhID0gMi40NSwgICAgICAgICAgICAgICAgICAgICAgICAgICMgTGF0ZW50IGhlYXQgb2YgdmFwb3JpemF0aW9uIGluIE1KLmtnXi0xCiAgbGF0X3JhZCA9IDM5Ljg4ICogcGkgLyAxODAsICAgICAgICAgICAgICMgTGF0aXR1ZGUgaW4gcmFkaWFucwogIEdzYyA9IDAuMDgyMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFNvbGFyIGNvbnN0YW50IGluIE1KLm1eLTIubWluXi0xCikKClBFVF9IYXJncmVhdmVzIDwtIGNhbGN1bGF0ZV9FVCgKICBkYXRhID0gUEVUX2RhdGEsCiAgY29uc3RhbnRzID0gY29uc3RhbnRzLAogIHRzID0gImRhaWx5IiwgICAgICAgICMgT3B0aW9uYWw7IGRlZmF1bHRzIHRvICJkYWlseSIKICBtZXNzYWdlID0gInllcyIsICAgICAjIE9wdGlvbmFsOyBwcmludHMgc3VtbWFyeSAKICBzYXZlLmNzdiA9ICJubyIgICAgICAjIE9wdGlvbmFsOyBkbyBub3Qgc2F2ZSByZXN1bHRzIHRvIGEgQ1NWCikKYGBgCgpOb3cgd2UnbGwgYWRkIGRhaWx5IEVUIGludG8gb3VyIG9yaWdpbmFsIGRhdGFmcmFtZToKCmBgYHtyfQpQRVRfbW0gPC0gUEVUX0hhcmdyZWF2ZXMkRVQuRGFpbHkKIyBwdXQgdGhlIGFwcHJveGltYXRlZCBQRVRfbW0gaW50byB0aGUgbGFyZ2VyIGluZGF0YSBkZgppbmRhdGEgPC0gY2JpbmQoaW5kYXRhLCBQRVRfbW0pICAjd2l0aCBjYmluZAoKaGVhZChpbmRhdGEpCmBgYAoKCmBgYHtyfQojIEFubnVhbCBzdW1tYXJ5IHN0YXRzCmluZGF0YV9hbmFseXNpcyA8LSBpbmRhdGEgJT4lCiAgc2VsZWN0KC1kYXRlKSAlPiUKICBncm91cF9ieSh3dHJfeXIpICU+JQogIHN1bW1hcmlzZSgKICAgIHdlaWdodGVkX3ByZWNpcC5tbSA9IHN1bSh3ZWlnaHRlZF9wcmVjaXAubW0sIG5hLnJtID0gVFJVRSksCiAgICBUbWVhbl9jID0gbWVhbihUbWVhbl9jLCBuYS5ybSA9IFRSVUUpLAogICAgc3dlLm1tX21heCA9IG1heCh3ZWlnaHRlZF9zd2UubW0sIG5hLnJtID0gVFJVRSksCiAgICBwY3VtdWwubW1fbWF4ID0gbWF4KHdlaWdodGVkX3BjdW11bC5tbSwgbmEucm0gPSBUUlVFKSwKICAgIFRtYXggPSBtYXgoVG1heF9jLCBuYS5ybSA9IFRSVUUpLAogICAgVG1pbiA9IG1pbihUbWluX2MsIG5hLnJtID0gVFJVRSksCiAgICBRX21tX3N1bSA9IHN1bShRX21tX2RheSwgbmEucm0gPSBUUlVFKSwKICAgIFBFVF9tbV9zdW0gPSBzdW0oUEVUX21tLCBuYS5ybSA9IFRSVUUpLCAKICAgIHJ1bm9mZl9pbnB1dC5tbSA9IHN1bShydW5vZmZfaW5wdXQubW0sIG5hLnJtID0gVFJVRSksCiAgKQoKaW5kYXRhX2FuYWx5c2lzCgpgYGAKCgpgYGB7cn0KIyBZb3VyIHNjcmlwdCBiZWxvdyBzaG91bGQgZml0IHlvdXIgdmFyaWFibGVzIGZyb20geW91ciBzdW1tYXJ5IGRhdGFmcmFtZSBhYm92ZS4gSGVyZSBpcyBhbiBleGFtcGxlIG9mIHdoYXQgYSBzaW1wbGUgd2F0ZXIgYmFsYW5jZSBtaWdodCBsb29rIGxpa2UgaWYgSSBuYW1lZCBteSBzdW1tYXJ5IGRhdGFmcmFtZSBpbmRhdGFfYW5hbHlzaXM6IAoKIyBDYWxjdWxhdGUgdGhlIHJlc2lkdWFsIHdhdGVyIGFmdGVyIGFjY291bnRpbmcgZm9yIFBFVCBhbmQgZGlzY2hhcmdlCnJlc2lkdWFsX3dhdGVyIDwtIGluZGF0YV9hbmFseXNpcyRydW5vZmZfaW5wdXQubW0gLSAoaW5kYXRhX2FuYWx5c2lzJFBFVF9tbV9zdW0gKyBpbmRhdGFfYW5hbHlzaXMkUV9tbV9zdW0pCgojIFZpZXcgdGhlIHJlc2lkdWFsIGZvciBlYWNoIHllYXIKcHJpbnQocmVzaWR1YWxfd2F0ZXIpCmBgYApCeSBzdWJ0cmFjdGluZyBkaXNjaGFyZ2UgYW5kIFBFVCBmcm9tIHJ1bm9mZl9pbnB1dCwgd2XigJlyZSBlc3NlbnRpYWxseSBleGFtaW5pbmcgdGhlIHJlc2lkdWFsIHdhdGVyLiBUaGlzIGNvdWxkIHJlcHJlc2VudCB0aGUgYW1vdW50IG9mIHdhdGVyIGF2YWlsYWJsZSB0byB0aGUgc3lzdGVtIGFmdGVyIGFjY291bnRpbmcgZm9yIHRoZSBkZW1hbmQgKFBFVCkgYW5kIHRoZSBvdXRmbG93IChkaXNjaGFyZ2UpLgoKYGBge3J9CiMgbWFrZSBsb25nIGZvcm0gd2l0aCBQRVRzdW0gYW5kIFBzdW0gYXMgdGhlIGtleS12YWx1ZSBwYWlycyAoZXhjbHVkZSB3dHJfeXIgZnJvbSApCmRhdF9zdW1fcGxvdCA8LSBpbmRhdGFfYW5hbHlzaXMgJT4lCiAgcGl2b3RfbG9uZ2VyKG5hbWVzX3RvID0gICJrZXkiLCB2YWx1ZXNfdG8gPSAgInZhbHVlIiwgLXd0cl95cikKCiMgYmFyIHBsb3QgcGYgUEVUIGFuZCBQIGZvciBlYWNoIHllYXIKZ2dwbG90KGRhdF9zdW1fcGxvdCwgYWVzKHggPSB3dHJfeXIsIHkgPSB2YWx1ZSwgZmlsbCA9IGtleSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgbGFicyh4ID0gIldhdGVyIFllYXIiLCB5ID0gIm1tL3llYXIiLCBmaWxsID0ge30pCmBgYAoKYGBge3J9CiMjIyBUSU1JTkcKIyBjcmVhdGUgd2Vla2x5IG1lYW5zIG9yIHRvdGFscyBmb3IgYm90aCB0aW1lIHNlcmllcyBhbmQgcGxvdCB0aGVtIHRvZ2V0aGVyIHRvIGRldGVybWluZSB0aGUgdGltaW5nIG9mIGVhY2ggCmRhdF93ZWVrbHkgPC0gaW5kYXRhICU+JQogIGdyb3VwX2J5KFdlZWsgPSB3ZWVrKGRhdGUpKSAlPiUKICBzdW1tYXJpc2UoCiAgICBQRVQgPSBzdW0oUEVUX21tKSwgCiAgICBQID0gc3VtKHJ1bm9mZl9pbnB1dC5tbSkKICAgICkgJT4lCiAgICBwaXZvdF9sb25nZXIobmFtZXNfdG8gPSAia2V5IiwgdmFsdWVzX3RvID0gInZhbHVlIiwgLVdlZWspCgojIGxpbmUgcGxvdCBvZiBQIGFuZCBQRVQgb24gYSB3ZWVrbHkgYmFzaXMuIFVzZSBkYXRfd2Vla2x5IGFzIHRoZSBkYXRhIHNvdXJjZS4KZ2dwbG90KGRhdF93ZWVrbHksIGFlcyh4ID0gV2VlaywgeSA9IHZhbHVlLCBjb2xvciA9IGtleSkpICsKICBnZW9tX2xpbmUoKSArICMgY3JlYXRlIGxpbmUgcGxvdAogIGxhYnMoeCA9ICJXZWVrIG9mIFllYXIiLCB5ID0gIm1tL3llYXIiLCBjb2xvciA9IHt9KQpgYGAKCiMgU2luZ2xlIG1vZGVsIHJ1bgpMb29rIGF0IHRoZSBwYXJhbWV0ZXJzIHRvIG1ha2Ugc3VyZSB5b3Uga25vdyB3aGF0IGVhY2ggcGFyYW1ldGVyIGRvZXMuIFRoZSB3YXkgdGhlIG1vZGVsIGlzIHNldCB1cCwgaXMgdGhhdCBldmVyeXRoaW5nIGlzIGhpZGRlbiBpbnNpZGUgYSBmdW5jdGlvbi4gVGhlIHVzZXIgKC0tPiB5b3UpIG9ubHkgZm9ybWF0cyB0aGUgaW5wdXQgZGF0YSBhbmQgcGFzc2VzIHRoZSBwYXJhbWV0ZXJzIHRvIHRoZSBmdW5jdGlvbi4gQWxsIG1vZGVsIG91dHB1dCBpcyBjb250YWluZWQgaW4gIm1vZGVsUnVuIi4gSWYgeW91IGxvb2sgYXQgdGhlIEVudmlyb25tZW50LCB5b3Ugd2lsbCBub3RpY2UgdGhhdCBtb2RlbFJ1biBpcyBhIGxpc3QuIEEgbGlzdCBpcyBldmVuIG1vcmUgZmxleGlibGUgaW4gdGVybXMgb2YgZGF0YSBzdG9yYWdlIHRoYW4gYSBkYXRhZnJhbWUgKGEgZGF0YWZyYW1lIGlzIGFjdHVhbGx5IGEgc3BlY2lhbCB0eXBlIG9mIGxpc3QuLi4pLiBXaGlsZSBsaXN0cyBhcmUgc3VwZXIgZmxleGlibGUsIHRoZXkgY2FuIGFsc28gYmUgbW9yZSBjdW1iZXJzb21lIHRvIGRlYWwgd2l0aC4gSSBpbmNsdWRlZCBzb21lIGNvZGUgdGhhdCB0YWtlcyB0aGUgb3V0cHV0IGZyb20gdGhlIG1vZGVsIHJ1biBhbmQgc2F2ZXMgYWxsIHRoZSBpbXBvcnRhbnQgYml0cyBhbmQgcGllY2VzIGluIGEgY29udmVuaWVudCBkYXRhZnJhbWUgY2FsbGVkIEhCVlJ1bi4gRm9yIHRoaXMgcGFydCAoc2luZ2xlIG1vZGVsIHJ1bnMpLCB5b3Ugd2lsbCByZWFsbHkgb25seSBuZWVkIHRoZSBkYXRhIGNvbnRhaW5lZCBpbiB0aGUgSEJWUnVuIGRhdGFmcmFtZS4KICAKIyBzaW5nbGUgbW9kZWwgZXhlY3V0aW9uCmBgYHtyfQojIHNldCB1cCB0aGUgcGFyYW1ldGVyIHZlY3Rvcgptb2RlbF9wYXJhbXMgPC0gYygKICAxLjA1LCAjIFNDRiBzbm93IGNvcnJlY3Rpb24gZmFjdG9yIFstXSAoZS5nLiwgMC45LTEuNSk7CiAgMS44MCwgIyBEREYgZGVncmVlIGRheSBmYWN0b3IgW21tL2RlZ0MvdGltZXN0ZXBdIChlLmcuLCAwLjAtNS4wIG1tL2RlZ0MvZGF5KTsKICAyLCAjIFRyIHRocmVzaG9sZCB0ZW1wZXJhdHVyZSBhYm92ZSB3aGljaCBwcmVjaXBpdGF0aW9uIGlzIHJhaW4gW2RlZ0NdIChlLmcuLCAxLjAtMy4wIGRlZ0MpOwogIDAsICMgVHMgdGhyZXNob2xkIHRlbXBlcmF0dXJlIGJlbG93IHdoaWNoIHByZWNpcGl0YXRpb24gaXMgc25vdyBbZGVnQ10gKGUuZy4sIC0zLjAtMS4wIGRlZ0MpOwogIC0wLjMzNiwgIyBUbSB0aHJlc2hvbGQgdGVtcGVyYXR1cmUgYWJvdmUgd2hpY2ggbWVsdCBzdGFydHMgW2RlZ0NdIChlLmcuLCAtMi4wLTIuMCBkZWdDKTsKICAwLjIsICMgTFByYXQgcGFyYW1ldGVyIHJlbGF0ZWQgdG8gdGhlIGxpbWl0IGZvciBwb3RlbnRpYWwgZXZhcG9yYXRpb24gWy1dIChlLmcuLCAwLjAtMS4wKTsKICAxMjEsICMgRkMgZmllbGQgY2FwYWNpdHksIGkuZS4sIG1heCBzb2lsIG1vaXN0dXJlIHN0b3JhZ2UgW21tXSAoZS5nLiwgMC02MDAgbW0pOwogIDIuNTIsICMgQkVUQSB0aGUgbm9uIGxpbmVhciBwYXJhbWV0ZXIgZm9yIHJ1bm9mZiBwcm9kdWN0aW9uIFstXSAoZS5nLiwgMC4wLTIwLjApOwogIDAuNDczLCAjIGswIHN0b3JhZ2UgY29lZmZpY2llbnQgZm9yIHZlcnkgZmFzdCByZXNwb25zZSBbdGltZXN0ZXBdIChlLmcuLCAwLjAtMi4wIGRheXMpOwogIDkuMDYsICMgazEgc3RvcmFnZSBjb2VmZmljaWVudCBmb3IgZmFzdCByZXNwb25zZSBbdGltZXN0ZXBdIChlLmcuLCAyLjAtMzAuMCBkYXlzKTsKICAxNDIsICMgazIgc3RvcmFnZSBjb2VmZmljaWVudCBmb3Igc2xvdyByZXNwb25zZSBbdGltZXN0ZXBdIChlLmcuLCAzMC4wLTI1MC4wIGRheXMpOwogIDUwLjEsICMgbHN1eiB0aHJlc2hvbGQgc3RvcmFnZSBzdGF0ZSwgaS5lLiwgdGhlIHZlcnkgZmFzdCByZXNwb25zZSBzdGFydCBpZiBleGNlZWRlZCBbbW1dIChlLmcuLCAxLjAtMTAwLjAgbW0pOwogIDIuMzgsICMgY3BlcmMgY29uc3RhbnQgcGVyY29sYXRpb24gcmF0ZSBbbW0vdGltZXN0ZXBdIChlLmcuLCAwLjAtOC4wIG1tL2RheSk7CiAgMTAsICMgYm1heCBtYXhpbXVtIGJhc2UgYXQgbG93IGZsb3dzIFt0aW1lc3RlcF0gKGUuZy4sIDAuMC0zMC4wIGRheXMpOwogIDI1ICMgY3JvdXRlIGZyZWUgc2NhbGluZyBwYXJhbWV0ZXIgW3RpbWVzdGVwXjIvbW1dIChlLmcuLCAwLjAtNTAuMCBkYXlzXjIvbW0pOwopCgoKIyBzZXQgdGltZSBwZXJpb2QKbW9kZWxfaW4gPC0gaW5kYXRhICU+JQogIGZpbHRlcihkYXRlID49IGFzX2RhdGUoIjIwMTctMTAtMDEiKSAmIGRhdGUgPD0gYXNfZGF0ZSgiMjAyMi0wOS0zMCIpKQoKIyBzZXQgdXAgdGhlIG1vZGVsCiMjIFRISVMgSVMgVEhFIEFDVFVBTCBNT0RFTCBFWEVDVVRJT04KbW9kZWxSdW4gPC0gVFVXbW9kZWwoCiAgcHJlYyA9IG1vZGVsX2luJHdlaWdodGVkX3ByZWNpcC5tbSwgIyBwcmVjaXAgaW5wdXQKICBhaXJ0ID0gbW9kZWxfaW4kVG1lYW5fYywgIyBhaXIgdGVtcCBpbnB1dAogIGVwID0gbW9kZWxfaW4kUEVUX21tLCAjIHBldCBpbnB1dAogIGFyZWEgPSAxLCAjIG9uZSB6b25lIGZvciB0aGUgZW50aXJlIHdhdGVyc2hlZAogIHBhcmFtID0gbW9kZWxfcGFyYW1zICMgaW5wdXQgbW9kZWwgcGFyYW1ldGVycwopCgoKIyBnZXQgYWxsIG91dHB1dHMgaW50byBhIG5pY2UgZGYKSEJWUnVuIDwtIHRpYmJsZSgKICBEYXRlID0gbW9kZWxfaW4kRGF0ZSwgIyBkYXRlCiAgUF9tbSA9IG1vZGVsUnVuJHByZWMsICMgcHJlY2lwCiAgVGFpciA9IG1vZGVsUnVuJGFpcnQsICMgYWlyIHRlbXAKICBTV0VvYnMgPSBtb2RlbF9pbiR3ZWlnaHRlZF9zd2UubW0sICMgb2JzZXJ2ZWQgc3dlCiAgUEVUID0gbW9kZWxSdW4kZXAsIyBwZXQKICBRb2JzID0gbW9kZWxfaW4kUV9tbV9kYXksICMgb2JzZXJ2ZWQgZGlzY2hhcmdlCiAgUXNpbSA9IG1vZGVsUnVuJHFbMSwgXSwgIyBzaW11bGF0ZWQgZGlzY2hhcmdlCiAgUXN1cmYgPSBtb2RlbFJ1biRxMFsxLCBdLCAjIHN1cmZhY2UgcnVub2ZmCiAgUXN1YnN1cmYgPSBtb2RlbFJ1biRxMVsxLCBdLCAjIHN1YnN1cmZhY2UgZmxvdwogIFFiYXNlID0gbW9kZWxSdW4kcTJbMSwgXSwgIyBncm91bmR3YXRlciBmbG93CiAgUmFpbiA9IG1vZGVsUnVuJHJhaW5bMSwgXSwgIyBzaW11bGF0ZWQgcmFpbgogIFNub3cgPSBtb2RlbFJ1biRzbm93WzEsIF0sICMgc2ltdWxhdGVkIHNub3dmYWxsCiAgTWVsdCA9IG1vZGVsUnVuJG1lbHRbMSwgXSwgIyBzaW11bGF0ZWQgbWVsdAogIFNXRXNpbSA9IG1vZGVsUnVuJHN3ZVsxLCBdLCAjIHNpbXVsYXRlZCBzd2UKICBTb2lsbW9pc3QgPSBtb2RlbFJ1biRtb2lzdFsxLCBdLCAjIHNpbXVsYXRlZCBzb2lsIHN0b3JhZ2UKICBBRVQgPSBtb2RlbFJ1biRldGFbMSwgXSwgIyBzaW11bGF0ZWQgZXZhcG90cmFuc3BpcmF0aW9uCiAgU3RvcmFnZVVwcGVyID0gbW9kZWxSdW4kc3V6WzEsIF0sICMgdXBwZXIgc3RvcmFnZSB2YWx1ZQogIFN0b3JhZ2VMb3dlciA9IG1vZGVsUnVuJHNselsxLCBdICMgbG93ZXIgc3RvcmFnZSB2YWx1ZQopCmBgYAoKIyMjICBPYmplY3RpdmUgZnVuY3Rpb25zCiMjIyMgS0dFCgpCZWZvcmUgd2UgY2FuIHN0YXJ0IHRyeWluZyB0byB0dW5lIG91ciBtb2RlbCB0byBsb29rIG1vcmUgbGlrZSB0aGUKb2JzZXJ2ZWQgZGlzY2hhcmdlIHJlY29yZCwgaXQgd291bGQgYmUgaGVscGZ1bCB0byBoYXZlIHNvbWUgc29ydCBvZgpxdWFudGlmaWVkIG1ldHJpYyBmb3IgaG93IHdlbGwgb3VyIG1vZGVsZWQgZGF0YSBmaXRzIHRoZSBtZWFzdXJlZCBkYXRhLgoKVGhlcmUgYXJlIG1hbnkgZGlmZmVyZW50IHdheXMgdG8gZG8gdGhpcywgYnV0IGRpc2N1c3Npb24gb2YgdGhlIHByb3MgYW5kIGNvbnMgb2YgdGhvc2UgYXBwcm9hY2hlcyBpcyBiZXlvbmQgdGhpcyBxdWljayBpbnRyb2R1Y3Rpb24gdG8gbW9kZWxpbmcuCgpIZXJlIHdlIHdpbGwgZGVtb25zdHJhdGUgdGhlIEtsaW5nLUd1cHRhIGVmZmljaWVuY3kgYm90aCBmb3IgcnVub2ZmIGFzIHdlbGwgYXMgZm9yIHN3ZS4KYGBge3J9CiMgc2VsZWN0IHRoZSBRb2JzIGFuZCBRc2ltIHRpbWVzZXJpZXMKIyBET04nVCBGT1JHRVQgVE8gRVhDTFVERSBUSEUgRklSU1QgWUVBUgpRb2JzIDwtIEhCVlJ1biRRb2JzWzM2NjpsZW5ndGgoSEJWUnVuJFFvYnMpXSAjIG9ic2VydmVkIHJ1bm9mZiBXSVRIT1VUIFdBUk0tVVAgUEVSSU9EClFzaW0gPC0gSEJWUnVuJFFzaW1bMzY2Omxlbmd0aChIQlZSdW4kUXNpbSldICMgc2ltdWxhdGVkIHJ1bm9mZiBXSVRIT1VUIFdBUk0tVVAgUEVSSU9ECgojIEtHRQprZ2Vfcl9xIDwtIGNvcihRb2JzLCBRc2ltKQprZ2VfYmV0YV9xIDwtIG1lYW4oUXNpbSkgLyBtZWFuKFFvYnMpCmtnZV9nYW1tYV9xIDwtIChzZChRc2ltKSAvIG1lYW4oUXNpbSkpIC8gKHNkKFFvYnMpIC8gbWVhbihRb2JzKSkKa2dlX3EgPC0gMSAtIHNxcnQoKGtnZV9yX3EgLSAxKV4yICsgKGtnZV9iZXRhX3EgLSAxKV4yICsgKGtnZV9nYW1tYV9xIC0gMSleMikKa2dlX3EKYGBgCgoKYGBge3J9CiMjIFNub3cgLSB3YXRlciBlcXVpdmFsZW50ClNXRW9icyA8LSBIQlZSdW4kU1dFb2JzWzM2NjpsZW5ndGgoSEJWUnVuJFNXRW9icyldICMgb2JzZXJ2ZWQgc3dlIFdJVEhPVVQgV0FSTS1VUCBQRVJJT0QKU1dFc2ltIDwtIEhCVlJ1biRTV0VzaW1bMzY2Omxlbmd0aChIQlZSdW4kU1dFb2JzKV0gIyBzaW11bGF0ZWQgc3dlIFdJVEhPVVQgV0FSTS1VUCBQRVJJT0QKCiMgS0dFCmtnZV9yX3N3ZSA8LSBjb3IoU1dFb2JzLCBTV0VzaW0pCmtnZV9iZXRhX3N3ZSA8LSBtZWFuKFNXRXNpbSkgLyBtZWFuKFNXRW9icykKa2dlX2dhbW1hX3N3ZSA8LSAoc2QoU1dFc2ltKSAvIG1lYW4oU1dFc2ltKSkgLyAoc2QoU1dFb2JzKSAvIG1lYW4oU1dFb2JzKSkKa2dlX3N3ZSA8LSAxIC0gc3FydCgoa2dlX3Jfc3dlIC0gMSleMiArIChrZ2VfYmV0YV9zd2UgLSAxKV4yICsgKGtnZV9nYW1tYV9zd2UgLSAxKV4yKQprZ2Vfc3dlCmBgYAoKIyMjIyBOU0UKTmFzaC1TdXRjbGlmZmUgRWZmaWNpZW5jeSAoTlNFKS4KCkJhc2ljYWxseSwgdGhlIE5TRSBsb29rcyBhdCBob3cgbXVjaCBiZXR0ZXIgeW91ciBtb2RlbCBydW4gZGlkIHRoYXQgaWYgeW91IGhhZCBqdXN0IHVzZWQgdGhlIG1lYW4gZGlzY2hhcmdlIGZvciB0aGUgZGF0YSByZWNvcmQgYXMgeW91cgoibW9kZWxsZWQgcmVzdWx0cyIuIEl0IGRvZXMgdGhpcyBieSBjb21wYXJpbmcgaG93IGZhciBvZmYgdGhlIG9ic2VydmVkIHZhbHVlcyB3aGVyZSBmcm9tIHRoZSBtZWFuIGRpc2NoYXJnZSB0byBob3cgZmFyIG9mZiB0aGUgbW9kZWxlZCB2YWx1ZXMgd2VyZSBmcm9tIHRoZSBvYnNlcnZlZCBkaXNjaGFyZ2UuCgpNYXRoZW1hdGljYWxseSwgTlNFIGlzIHRoZSBzdW0gb2YgdGhlIHNxdWFyZWQgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUKbW9kZWxlZCBhbmQgb2JzZXJ2ZWQgZGlzY2hhcmdlIGRpdmlkZWQgYnkgdGhlIHN1bSBvZiB0aGUgc3F1YXJlZApkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBvYnNlcnZlZCBhbmQgbWVhbiBkaXNjaGFyZ2UsIHN1YnRyYWN0ZWQgYnkgMS4KCiQkCk5TRSA9IDEgLSBcZnJhY3tcc3VtX3t0ID0gMX1ee1R9eyhRX21edCAtIFFfb150KV4yfX17XHN1bV97dCA9IDF9XntUfXsoUV9vXnQgLSBcYmFye1Ffb30pXjJ9fQokJCBXaGVyZSAkUV9tXnQkIGlzIG1vZGVsZWQgZGlzY2hhcmdlIGF0IHRpbWUgdCwgJFFfb150JCBpcyBvYnNlcnZlZApkaXNjaGFyZ2UgYXQgdGltZSB0LCBhbmQgJFxiYXJ7UV9vfSQgaXMgbWVhbiBvYnNlcnZlZCBkaXNjaGFyZ2UuCgpgYGB7cn0KI0NhbGN1bGF0ZSBOU0UgZm9yIHNub3csIFNXRSBpcyBtb2RlbGVkLCBTVEEyIGlzIG1lYXN1cmVkCk5TRV9RIDwtIDEgLSAoKHN1bSgoSEJWUnVuJFFvYnMgLSBIQlZSdW4kUXNpbSkgXiAyKSkgLyAKICAgICAgICAgICAgICAgICBzdW0oKEhCVlJ1biRRc2ltIC0gbWVhbihIQlZSdW4kUXNpbSkpIF4gMikpCgpOU0VfUQpgYGAKCmBgYHtyfQojQ2FsY3VsYXRlIE5TRSBmb3Igc25vdywgU1dFIGlzIG1vZGVsZWQsIFNUQTIgaXMgbWVhc3VyZWQKTlNFc25vIDwtIDEgLSAoKHN1bSgoSEJWUnVuJFNXRW9icyAtIEhCVlJ1biRTV0VzaW0pIF4gMikpIC8gCiAgICAgICAgICAgICAgICAgc3VtKChIQlZSdW4kU1dFc2ltIC0gbWVhbihIQlZSdW4kU1dFc2ltKSkgXiAyKSkKCk5TRXNubwpgYGAKIyMgQ2FsaWJyYXRlIEhCViBtYW51YWxseQoKV29vaG9vISBXZSBjYW4gbm93IHJ1biBvdXIgbW9kZWwgYW5kIGFzc2VzcyBob3cgd2VsbCBpdCBpcyB3b3JraW5nIQoKTm93LCBsZXQncyBzZWUgaG93IHdlbGwgd2UgY2FuIGdldCBpdCB0byB3b3JrLiBUaGUgY29kZSBiZWxvdyBydW5zIHRoZSBtb2RlbCwgcHJvZHVjZXMgYSBwbG90LCBhbmQgY2FsY3VsYXRlcyB0aGUgTlNFIGJhc2VkIG9uIGRpc2NoYXJnZS4KCmBgYHtyfQoKIyBzZXQgdXAgdGhlIHBhcmFtZXRlciB2ZWN0b3IKbW9kZWxfcGFyYW1zIDwtIGMoCiAgMS4wNSwgIyBTQ0Ygc25vdyBjb3JyZWN0aW9uIGZhY3RvciBbLV0gKGUuZy4sIDAuOS0xLjUpOwogIDEuODAsICMgRERGIGRlZ3JlZSBkYXkgZmFjdG9yIFttbS9kZWdDL3RpbWVzdGVwXSAoZS5nLiwgMC4wLTUuMCBtbS9kZWdDL2RheSk7CiAgMiwgIyBUciB0aHJlc2hvbGQgdGVtcGVyYXR1cmUgYWJvdmUgd2hpY2ggcHJlY2lwaXRhdGlvbiBpcyByYWluIFtkZWdDXSAoZS5nLiwgMS4wLTMuMCBkZWdDKTsKICAwLCAjIFRzIHRocmVzaG9sZCB0ZW1wZXJhdHVyZSBiZWxvdyB3aGljaCBwcmVjaXBpdGF0aW9uIGlzIHNub3cgW2RlZ0NdIChlLmcuLCAtMy4wLTEuMCBkZWdDKTsKICAtMC4zMzYsICMgVG0gdGhyZXNob2xkIHRlbXBlcmF0dXJlIGFib3ZlIHdoaWNoIG1lbHQgc3RhcnRzIFtkZWdDXSAoZS5nLiwgLTIuMC0yLjAgZGVnQyk7CiAgMC4yLCAjIExQcmF0IHBhcmFtZXRlciByZWxhdGVkIHRvIHRoZSBsaW1pdCBmb3IgcG90ZW50aWFsIGV2YXBvcmF0aW9uIFstXSAoZS5nLiwgMC4wLTEuMCk7CiAgMTIxLCAjIEZDIGZpZWxkIGNhcGFjaXR5LCBpLmUuLCBtYXggc29pbCBtb2lzdHVyZSBzdG9yYWdlIFttbV0gKGUuZy4sIDAtNjAwIG1tKTsKICAyLjUyLCAjIEJFVEEgdGhlIG5vbiBsaW5lYXIgcGFyYW1ldGVyIGZvciBydW5vZmYgcHJvZHVjdGlvbiBbLV0gKGUuZy4sIDAuMC0yMC4wKTsKICAwLjQ3MywgIyBrMCBzdG9yYWdlIGNvZWZmaWNpZW50IGZvciB2ZXJ5IGZhc3QgcmVzcG9uc2UgW3RpbWVzdGVwXSAoZS5nLiwgMC4wLTIuMCBkYXlzKTsKICA5LjA2LCAjIGsxIHN0b3JhZ2UgY29lZmZpY2llbnQgZm9yIGZhc3QgcmVzcG9uc2UgW3RpbWVzdGVwXSAoZS5nLiwgMi4wLTMwLjAgZGF5cyk7CiAgMTQyLCAjIGsyIHN0b3JhZ2UgY29lZmZpY2llbnQgZm9yIHNsb3cgcmVzcG9uc2UgW3RpbWVzdGVwXSAoZS5nLiwgMzAuMC0yNTAuMCBkYXlzKTsKICA1MC4xLCAjIGxzdXogdGhyZXNob2xkIHN0b3JhZ2Ugc3RhdGUsIGkuZS4sIHRoZSB2ZXJ5IGZhc3QgcmVzcG9uc2Ugc3RhcnQgaWYgZXhjZWVkZWQgW21tXSAoZS5nLiwgMS4wLTEwMC4wIG1tKTsKICAyLjM4LCAjIGNwZXJjIGNvbnN0YW50IHBlcmNvbGF0aW9uIHJhdGUgW21tL3RpbWVzdGVwXSAoZS5nLiwgMC4wLTguMCBtbS9kYXkpOwogIDEwLCAjIGJtYXggbWF4aW11bSBiYXNlIGF0IGxvdyBmbG93cyBbdGltZXN0ZXBdIChlLmcuLCAwLjAtMzAuMCBkYXlzKTsKICAyNSAjIGNyb3V0ZSBmcmVlIHNjYWxpbmcgcGFyYW1ldGVyIFt0aW1lc3RlcF4yL21tXSAoZS5nLiwgMC4wLTUwLjAgZGF5c14yL21tKTsKKQoKCiMgc2V0IHRpbWUgcGVyaW9kCm1vZGVsX2luIDwtIGluZGF0YSAlPiUKICBmaWx0ZXIoZGF0ZSA+PSBhc19kYXRlKCIyMDE3LTEwLTAxIikgJiBkYXRlIDw9IGFzX2RhdGUoIjIwMjItMDktMzAiKSkKCiMgc2V0IHVwIHRoZSBtb2RlbAojIyBUSElTIElTIFRIRSBBQ1RVQUwgTU9ERUwgRVhFQ1VUSU9OCm1vZGVsUnVuIDwtIFRVV21vZGVsKAogIHByZWMgPSBtb2RlbF9pbiR3ZWlnaHRlZF9wcmVjaXAubW0sICMgcHJlY2lwIGlucHV0CiAgYWlydCA9IG1vZGVsX2luJFRtZWFuX2MsICMgYWlyIHRlbXAgaW5wdXQKICBlcCA9IG1vZGVsX2luJFBFVF9tbSwgIyBwZXQgaW5wdXQKICBhcmVhID0gMSwgIyBvbmUgem9uZSBmb3IgdGhlIGVudGlyZSB3YXRlcnNoZWQKICBwYXJhbSA9IG1vZGVsX3BhcmFtcyAjIGlucHV0IG1vZGVsIHBhcmFtZXRlcnMKKQoKCiMgZ2V0IGFsbCBvdXRwdXRzIGludG8gYSBuaWNlIGRmCkhCVlJ1biA8LSB0aWJibGUoCiAgRGF0ZSA9IG1vZGVsX2luJERhdGUsICMgZGF0ZQogIFBfbW0gPSBtb2RlbFJ1biRwcmVjLCAjIHByZWNpcAogIFRhaXIgPSBtb2RlbFJ1biRhaXJ0LCAjIGFpciB0ZW1wCiAgU1dFb2JzID0gbW9kZWxfaW4kd2VpZ2h0ZWRfc3dlLm1tLCAjIG9ic2VydmVkIHN3ZQogIFBFVCA9IG1vZGVsUnVuJGVwLCMgcGV0CiAgUW9icyA9IG1vZGVsX2luJFFfbW1fZGF5LCAjIG9ic2VydmVkIGRpc2NoYXJnZQogIFFzaW0gPSBtb2RlbFJ1biRxWzEsIF0sICMgc2ltdWxhdGVkIGRpc2NoYXJnZQogIFFzdXJmID0gbW9kZWxSdW4kcTBbMSwgXSwgIyBzdXJmYWNlIHJ1bm9mZgogIFFzdWJzdXJmID0gbW9kZWxSdW4kcTFbMSwgXSwgIyBzdWJzdXJmYWNlIGZsb3cKICBRYmFzZSA9IG1vZGVsUnVuJHEyWzEsIF0sICMgZ3JvdW5kd2F0ZXIgZmxvdwogIFJhaW4gPSBtb2RlbFJ1biRyYWluWzEsIF0sICMgc2ltdWxhdGVkIHJhaW4KICBTbm93ID0gbW9kZWxSdW4kc25vd1sxLCBdLCAjIHNpbXVsYXRlZCBzbm93ZmFsbAogIE1lbHQgPSBtb2RlbFJ1biRtZWx0WzEsIF0sICMgc2ltdWxhdGVkIG1lbHQKICBTV0VzaW0gPSBtb2RlbFJ1biRzd2VbMSwgXSwgIyBzaW11bGF0ZWQgc3dlCiAgU29pbG1vaXN0ID0gbW9kZWxSdW4kbW9pc3RbMSwgXSwgIyBzaW11bGF0ZWQgc29pbCBzdG9yYWdlCiAgQUVUID0gbW9kZWxSdW4kZXRhWzEsIF0sICMgc2ltdWxhdGVkIGV2YXBvdHJhbnNwaXJhdGlvbgogIFN0b3JhZ2VVcHBlciA9IG1vZGVsUnVuJHN1elsxLCBdLCAjIHVwcGVyIHN0b3JhZ2UgdmFsdWUKICBTdG9yYWdlTG93ZXIgPSBtb2RlbFJ1biRzbHpbMSwgXSAjIGxvd2VyIHN0b3JhZ2UgdmFsdWUKKQoKI1RyaW0gb3V0IHRoZSB3YXJtIHVwIHBlcmlvZApPdXRUcmltIDwtIEhCVlJ1biAlPiUgc2xpY2UoMzY2Om4oKSkKCiNDYWxjdWxhdGUgTlNFCk5TRSA8LSAxIC0gKChzdW0oKE91dFRyaW0kUXNpbSAtIE91dFRyaW0kUW9icykgXiAyKSkgLyAKICAgICAgICAgICAgICAgICBzdW0oKE91dFRyaW0kUW9icyAtIG1lYW4oT3V0VHJpbSRRb2JzKSkgXiAyKSkKCnByaW50KE5TRSkKYGBgCgojIyBQbG90IG9ic2VydmVkIGFuZCBtb2RlbCBzaW11bGF0aW9ucwoKR2VuZXJhdGUgcGxvdHMgdGhhdCBpbmNsdWRlIGFuZCBjb21wYXJlIHRoZSBkaWZmZXJlbnQgbW9kZWxlZCBmbHV4ZXMgZnJvbSB5b3VyIGJlc3QgTlNFLiBTb21lIG9mIHRob3NlIGZsdXhlcyBjYW4gYmUgaW1tZWRpYXRlbHkgY29tcGFyZWQgdG8gb2JzZXJ2ZWQgZGF0YSAoZS5nLiwgcnVub2ZmIG9yIFNXRSksIHdoaWxlIG90aGVycyBvbmx5IGV4aXN0IGluIHNpbXVsYXRlZCBmb3JtIChlLmcuLCBzdG9yYWdlcyBvciBvdXRmbG93cyBvZiB0aGUgdmFyaW91cyBydW5vZmYgY29tcG9uZW50cykgYW5kIG5lZWQgdG8gYmUgYXNzZXNzZWQgd2l0aCB0aGUgcGVyY2VwdHVhbCBtb2RlbCBpbiBtaW5kLiAgCk1ha2Ugc3VyZSB0aGF0IHRoZSBheGVzIGFyZSBwcm9wZXJseSBsYWJlbGVkIHdoZW4geW91IGNyZWF0ZSBwbG90cy4gVGhlIHNjcmlwdCBiZWxvdyB3aWxsIGdldCB5b3Ugc3RhcnRlZC4gCgpgYGB7cn0KIyBBZGQgZGF0ZSB0byB0aGUgSEJWUnVuIHJlc3VsdCB0aWJibGUKSEJWUnVuIDwtIGFzLmRhdGEuZnJhbWUoSEJWUnVuKQpIQlZSdW4kRGF0ZSA8LSBtb2RlbF9pbiRkYXRlCnN0cihIQlZSdW4pCgojIFJvdW5kIHRoZSBOU0UgZm9yIGRpc3BsYXkKbnNlX2xhYmVsIDwtIHBhc3RlKCJOU0UgPSIsIHJvdW5kKE5TRSwgMykpCgpxX3Bsb3QgPC0gZ2dwbG90KGRhdGEgPSBIQlZSdW4pICsKICBnZW9tX2xpbmUoYWVzKHggPSBEYXRlLCB5ID0gUW9icywgY29sb3IgPSAiUW9icyIpKSArICAjIFFvYnMKICBnZW9tX2xpbmUoYWVzKHggPSBEYXRlLCB5ID0gUXNpbSwgY29sb3IgPSAiUXNpbSIpKSArICAjIFFzaW0KICBhbm5vdGF0ZSgidGV4dCIsCiAgICAgICAgICAgeCA9IG1heChIQlZSdW4kRGF0ZSkgLSAxMDAsICAjIE1vdmUgbGFiZWwgIyBkYXlzIGZyb20gdGhlIGVuZAogICAgICAgICAgIHkgPSBtYXgoSEJWUnVuJFFvYnMsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgbGFiZWwgPSBuc2VfbGFiZWwsCiAgICAgICAgICAgaGp1c3QgPSAxLCB2anVzdCA9IDEuNSwgc2l6ZSA9IDUsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9ICJRIChtbS9kYXkpIiwgY29sb3IgPSBOVUxMLCB0aXRsZSA9ICJRb2JzIGFuZCBRc2ltIikKCgojIE1ha2UgaXQgaW50ZXJhY3RpdmUgd2l0aCBwbG90bHkKZ2dwbG90bHkocV9wbG90KQoKYGBgCgojIyMgTW9yZSBwbG90cwoKYGBge3J9CiMgU3dlCnN3ZV9wbG90IDwtIGdncGxvdChkYXRhID0gSEJWUnVuKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gRGF0ZSwgeSA9IFNXRW9icywgY29sb3IgPSAiU1dFb2JzIikpICsgIyBTV0VvYnMKICBnZW9tX2xpbmUoYWVzKEJMQU5LKSkgKyAjIFNXRXNpbQogIGxhYnMoeCA9IHt9LCB5ID0gIlNXRSAobW0pIiwgY29sb3IgPSB7fSkKZ2dwbG90bHkoc3dlX3Bsb3QpCmBgYAoKCmBgYHtyfQojIFEwLCBRMSwgUTIKcV9idWNrZXRfcGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IEhCVlJ1bikgKwogIGdlb21fbGluZShhZXMoeCA9IERhdGUsIHkgPSBRc3VyZiwgY29sb3IgPSAiUXN1cmYiKSkgKyAjIFFzdXJmCiAgZ2VvbV9saW5lKGFlcyh4ID0gRGF0ZSwgeSA9IFFzdWJzdXJmLCBjb2xvciA9ICJRc3Vic3VyZiIpKSArICNRc3Vic3VyZgogIGdlb21fbGluZShhZXMoeCA9IERhdGUsIHkgPSBRYmFzZSwgY29sb3IgPSAiUWJhc2UiKSkgKyAjIFFiYXNlCiAgbGFicyh4ID0ge30sIHkgPSAiUSAobW0pIiwgY29sb3IgPSB7fSwgdGl0bGUgPSAiUTAsIFExLCBhbmQgUTIiKQpnZ3Bsb3RseShxX2J1Y2tldF9wbG90KQpgYGA=